my DataSource Method never called for some reason - ios

im using small library called RSKImageCropView and it got Delegate and DataSource methods. The Delegate called as expecterd but the DataSource dont.
here is how i configure it:
let someImage = UIImage(data: blob)
let controller: RSKImageCropViewController = RSKImageCropViewController(image: someImage!, cropMode: RSKImageCropMode.Circle)
controller.delegate = self
controller.dataSource = self
self.navigationController?.pushViewController(controller, animated: true)
here is Delegate and DataSource methods:
extension GameViewController: RSKImageCropViewControllerDataSource {
func imageCropViewControllerCustomMaskRect(controller: RSKImageCropViewController) -> CGRect {
let rect = CGRect(x: 30, y: 30, width: 30, height: 30)
return rect
}
func imageCropViewControllerCustomMovementRect(controller: RSKImageCropViewController) -> CGRect {
return controller.maskRect
}
func imageCropViewControllerCustomMaskPath(controller: RSKImageCropViewController) -> UIBezierPath {
let rect = controller.maskRect;
let point1 = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
let point2 = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
let point3 = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
let triangle = UIBezierPath()
triangle.moveToPoint(point1)
triangle.addLineToPoint(point2)
triangle.addLineToPoint(point3)
triangle.closePath()
print("yay")
return triangle
}
}
extension GameViewController: RSKImageCropViewControllerDelegate {
func imageCropViewControllerDidCancelCrop(controller: RSKImageCropViewController) {
self.navigationController?.popViewControllerAnimated(true)
}
func imageCropViewController(controller: RSKImageCropViewController, didCropImage croppedImage: UIImage, usingCropRect cropRect: CGRect, rotationAngle: CGFloat) {
NSNotificationCenter.defaultCenter().postNotificationName("choseImage", object: croppedImage)
self.navigationController?.popViewControllerAnimated(true)
}
}

I believe you need to set the controller's cropMode to Custom for methods like imageCropViewControllerCustomMaskPath to be called, so try changing your instantiation to something like:
let controller: RSKImageCropViewController = RSKImageCropViewController(image: someImage!, cropMode: RSKImageCropMode.Custom)

Related

Resize and Rotate image Annotation added in pdf

Using this Link I have added signature image annotation in PDF file
But i could not find any guide for how to rotate and resize image annotation using the button added on top of annotation image like shown in the image.
What i want to do is:
want to scale/resize signature image(make it small or large by adding this resize button)
want to rotate signature image
For Pinch to zoom i am adding pinch gesture to PDFView but that gesture zoom in / zoom out the main pdf.tried to fix it by below code but not worked.
#objc func scale(sender : UIPinchGestureRecognizer) {
print("----------Scale----------")
let touchLocation = sender.location(in: pdfContainerView)
guard let page = pdfContainerView.page(for: touchLocation, nearest: true)
else {
return
}
let locationOnPage = pdfContainerView.convert(touchLocation, to: page)
switch sender.state {
case .began:
guard let annotation = page.annotation(at: locationOnPage) else {
return
}
if annotation.isKind(of: ImageStampAnnotation.self) {
currentlySelectedAnnotation = annotation
// to disable pinch gesture for pdfview but it is not working
pdfContainerView.minScaleFactor = pdfContainerView.scaleFactor
pdfContainerView.maxScaleFactor = pdfContainerView.scaleFactor
}
case .changed,.ended:
guard let annotation = currentlySelectedAnnotation else {
return
}
let initialBounds = annotation.bounds
//scale annotation
case .cancelled:
break
default:
break
}
}
Thanks in advance!!
I tried to implement rotation, zoom, pan on PDFview.
I created a new view above the PDFAnnotation and then moved, rotated, and scaled the view as above.
about PDFAnnotation:
class PDFImageAnnotation: PDFAnnotation {
let image: UIImage
let originalBounds: CGRect
/// 0 - 360
var angle: CGFloat = 0 {
didSet {
// reload annotation
shouldDisplay = true
}
}
/// scale annotation
var scale: CGFloat = 1 {
didSet {
// Scale on the original size
let width = originalBounds.width * scale
let height = originalBounds.height * scale
// move origin
let x = bounds.origin.x - (width - bounds.width)/2
let y = bounds.origin.y - (height - bounds.height)/2
print("new ---- \(CGRect(x: x, y: y, width: width, height: height))")
// Setting the bounds will automatically re-render
bounds = CGRect(x: x, y: y, width: width, height: height)
}
}
/// move center point
var center: CGPoint = .zero {
didSet {
let x = center.x - bounds.width/2.0
let y = center.y - bounds.height/2.0
// Setting the bounds will automatically re-render
bounds = CGRect(origin: CGPoint(x: x, y: y), size: bounds.size)
}
}
public init(bounds: CGRect, image: UIImage) {
self.image = image
originalBounds = bounds
super.init(bounds: bounds, forType: .ink, withProperties: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(with box: PDFDisplayBox, in context: CGContext) {
super.draw(with: box, in: context)
print("PDFImageAnnotation bounds - \(bounds)")
guard let page = page else {
return
}
UIGraphicsPushContext(context)
context.saveGState()
// rotate annotation
// The origin of the annotation is always at the initial position
let translateX = bounds.width/2 + bounds.origin.x
let translateY = bounds.height/2 + bounds.origin.y
// The page has its own rotation Angle
let newAngle = angle + CGFloat(page.rotation)
context.translateBy(x: translateX, y: translateY)
context.rotate(by: newAngle*(CGFloat.pi/180.0))
context.translateBy(x: -translateX, y: -translateY)
// draw image
if let cgImage = image.cgImage {
context.draw(cgImage, in: bounds)
}
context.restoreGState()
UIGraphicsPopContext()
}
}
use:
extension ViewController: PDFSignAnnotationViewDelegate {
func signAnnotationView(_ view: PDFSignAnnotationView, didMove point: CGPoint) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
let locationOnPage = self.pdfContainerView.convert(point, to: page)
imageAnnotation.center = locationOnPage
}
func signAnnotationView(_ view: PDFSignAnnotationView, didScale scale: CGFloat) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
imageAnnotation.scale = scale
}
func signAnnotationView(_ view: PDFSignAnnotationView, didRotate angle: CGFloat) {
guard let page = pdfContainerView.currentPage,
let imageAnnotation = page.annotations.filter({$0.userName == view.identity}).first as? PDFImageAnnotation else {
return
}
print("didRotate - \(angle)")
imageAnnotation.angle = -angle
}
func signAnnotationView(_ view: PDFSignAnnotationView, close identity: String) {
guard let page = pdfContainerView.currentPage else {
return
}
guard let annotation = page.annotations.filter({$0.userName == identity}).first else {
return
}
page.removeAnnotation(annotation)
}
}
TODO:
Move and zoom the page without sending changes to the upper view
I refer to here: https://medium.com/#rajejones/add-a-signature-to-pdf-using-pdfkit-with-swift-7f13f7faad3e
Demo https://github.com/roMummy/PDFSignature/tree/master/PDFSignature

use class in swiftui

I have these two classes which are for having the custom tapbar, I would like to use them in swiftUI how can I do? I used these wrappers but once implemented the class in the ContentView does not appear to me
I would like to do everything in swiftUI so I would prefer not to use storyboards in the implementation. it's possible ?
//SwiftUI class. I want to use this view already done in SwiftUI
HStack {
SHCircleBarControllerView()
SHCircleBarView()
}
//Class Swift 4
import UIKit
import SwiftUI
struct SHCircleBarControllerView : UIViewControllerRepresentable {
typealias UIViewControllerType = SHCircleBarController
func makeCoordinator() -> SHCircleBarControllerView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<SHCircleBarControllerView>) -> SHCircleBarController {
return SHCircleBarController()
}
func updateUIViewController(_ uiViewController: SHCircleBarController, context: UIViewControllerRepresentableContext<SHCircleBarControllerView>) {
}
class Coordinator : NSObject {
var parent : SHCircleBarControllerView
init(_ viewController : SHCircleBarControllerView){
self.parent = viewController
}
}
}
class SHCircleBarController: UITabBarController {
fileprivate var shouldSelectOnTabBar = true
private var circleView : UIView!
private var circleImageView: UIImageView!
open override var selectedViewController: UIViewController? {
willSet {
guard shouldSelectOnTabBar, let newValue = newValue else {
shouldSelectOnTabBar = true
return
}
guard let tabBar = tabBar as? SHCircleBar, let index = viewControllers?.firstIndex(of: newValue) else {return}
tabBar.select(itemAt: index, animated: true)
}
}
open override var selectedIndex: Int {
willSet {
guard shouldSelectOnTabBar else {
shouldSelectOnTabBar = true
return
}
guard let tabBar = tabBar as? SHCircleBar else {
return
}
tabBar.select(itemAt: selectedIndex, animated: true)
}
}
open override func viewDidLoad() {
super.viewDidLoad()
let tabBar = SHCircleBar()
self.setValue(tabBar, forKey: "tabBar")
self.circleView = UIView(frame: .zero)
circleView.layer.cornerRadius = 30
circleView.backgroundColor = .white
circleView.isUserInteractionEnabled = false
self.circleImageView = UIImageView(frame: .zero)
circleImageView.layer.cornerRadius = 30
circleImageView.isUserInteractionEnabled = false
circleImageView.contentMode = .center
circleView.addSubview(circleImageView)
self.view.addSubview(circleView)
let tabWidth = self.view.bounds.width / CGFloat(self.tabBar.items?.count ?? 4)
circleView.frame = CGRect(x: tabWidth / 2 - 30, y: self.tabBar.frame.origin.y - 40, width: 60, height: 60)
circleImageView.frame = self.circleView.bounds
}
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
circleImageView.image = image(with: self.tabBar.selectedItem?.image ?? self.tabBar.items?.first?.image, scaledTo: CGSize(width: 30, height: 30))
}
private var _barHeight: CGFloat = 74
open var barHeight: CGFloat {
get {
if #available(iOS 11.0, *) {
return _barHeight + view.safeAreaInsets.bottom
} else {
return _barHeight
}
}
set {
_barHeight = newValue
updateTabBarFrame()
}
}
private func updateTabBarFrame() {
var tabFrame = self.tabBar.frame
tabFrame.size.height = barHeight
tabFrame.origin.y = self.view.frame.size.height - barHeight
self.tabBar.frame = tabFrame
tabBar.setNeedsLayout()
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateTabBarFrame()
}
open override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
super.viewSafeAreaInsetsDidChange()
}
updateTabBarFrame()
}
open override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard let idx = tabBar.items?.firstIndex(of: item) else { return }
if idx != selectedIndex, let controller = viewControllers?[idx] {
shouldSelectOnTabBar = false
selectedIndex = idx
let tabWidth = self.view.bounds.width / CGFloat(self.tabBar.items!.count)
UIView.animate(withDuration: 0.3) {
self.circleView.frame = CGRect(x: (tabWidth * CGFloat(idx) + tabWidth / 2 - 30), y: self.tabBar.frame.origin.y - 15, width: 60, height: 60)
}
UIView.animate(withDuration: 0.15, animations: {
self.circleImageView.alpha = 0
}) { (_) in
self.circleImageView.image = self.image(with: item.image, scaledTo: CGSize(width: 30, height: 30))
UIView.animate(withDuration: 0.15, animations: {
self.circleImageView.alpha = 1
})
}
delegate?.tabBarController?(self, didSelect: controller)
}
}
private func image(with image: UIImage?, scaledTo newSize: CGSize) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(newSize, _: false, _: 0.0)
image?.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
You can set up your custom UIViewControllers inside the SHCircleBarController through the viewControllers property.
In SHCircleBarController
open override func viewDidLoad() {
super.viewDidLoad()
...
viewControllers = [ViewController(), ViewController2()]
}
Your other UIViewControllers
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
}
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
}
}
This is the result

how to use location for Eureka pod for iOS form?

I have added various filed successfully using Eureka form builder for iOS but getting an error while adding locationrow
I imported following framework still getting the issue
import UIKit
import CoreLocation
import MapKit
import Eureka
is there any other way to use locationrow in eureka?
you will have to first add LocationRow in your pod file and then update your pod file so that cocoapod downloads LocationRow. build the project once to refresh the project with the new files.
and then in the above class import LocationRow
LocationRow (Included as custom row in the example project)
These words were taken from https://github.com/xmartlabs/Eureka
So far Location Row can't be included into Eureka framework cause it's required MapKit or something similar, that's why eureka community created separated class/object.
I created a separate folder/file for the location code
import Foundation
import UIKit
import MapKit
import Eureka
//MARK: LocationRow
public final class LocationRow: OptionsRow<PushSelectorCell<CLLocation>>, PresenterRowType, RowType {
public typealias PresenterRow = MapViewController
/// Defines how the view controller will be presented, pushed, etc.
public var presentationMode: PresentationMode<PresenterRow>?
/// Will be called before the presentation occurs.
public var onPresentCallback: ((FormViewController, PresenterRow) -> Void)?
public required init(tag: String?) {
super.init(tag: tag)
presentationMode = .show(controllerProvider: ControllerProvider.callback { return MapViewController(){ _ in } }, onDismiss: { vc in _ = vc.navigationController?.popViewController(animated: true) })
displayValueFor = {
guard let location = $0 else { return "" }
let fmt = NumberFormatter()
fmt.maximumFractionDigits = 4
fmt.minimumFractionDigits = 4
let latitude = fmt.string(from: NSNumber(value: location.coordinate.latitude))!
let longitude = fmt.string(from: NSNumber(value: location.coordinate.longitude))!
return "\(latitude), \(longitude)"
}
}
/**
Extends `didSelect` method
*/
public override func customDidSelect() {
super.customDidSelect()
guard let presentationMode = presentationMode, !isDisabled else { return }
if let controller = presentationMode.makeController() {
controller.row = self
controller.title = selectorTitle ?? controller.title
onPresentCallback?(cell.formViewController()!, controller)
presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!)
} else {
presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!)
}
}
/**
Prepares the pushed row setting its title and completion callback.
*/
public override func prepare(for segue: UIStoryboardSegue) {
super.prepare(for: segue)
guard let rowVC = segue.destination as? PresenterRow else { return }
rowVC.title = selectorTitle ?? rowVC.title
rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback
onPresentCallback?(cell.formViewController()!, rowVC)
rowVC.row = self
}
}
public class MapViewController : UIViewController, TypedRowControllerType, MKMapViewDelegate {
public var row: RowOf<CLLocation>!
public var onDismissCallback: ((UIViewController) -> ())?
lazy var mapView : MKMapView = { [unowned self] in
let v = MKMapView(frame: self.view.bounds)
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return v
}()
lazy var pinView: UIImageView = { [unowned self] in
let v = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
v.image = UIImage(named: "map_pin", in: Bundle(for: MapViewController.self), compatibleWith: nil)
v.image = v.image?.withRenderingMode(.alwaysTemplate)
v.tintColor = self.view.tintColor
v.backgroundColor = .clear
v.clipsToBounds = true
v.contentMode = .scaleAspectFit
v.isUserInteractionEnabled = false
return v
}()
let width: CGFloat = 10.0
let height: CGFloat = 5.0
lazy var ellipse: UIBezierPath = { [unowned self] in
let ellipse = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: self.width, height: self.height))
return ellipse
}()
lazy var ellipsisLayer: CAShapeLayer = { [unowned self] in
let layer = CAShapeLayer()
layer.bounds = CGRect(x: 0, y: 0, width: self.width, height: self.height)
layer.path = self.ellipse.cgPath
layer.fillColor = UIColor.gray.cgColor
layer.fillRule = .nonZero
layer.lineCap = .butt
layer.lineDashPattern = nil
layer.lineDashPhase = 0.0
layer.lineJoin = .miter
layer.lineWidth = 1.0
layer.miterLimit = 10.0
layer.strokeColor = UIColor.gray.cgColor
return layer
}()
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nil, bundle: nil)
}
convenience public init(_ callback: ((UIViewController) -> ())?){
self.init(nibName: nil, bundle: nil)
onDismissCallback = callback
}
public override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView)
mapView.delegate = self
mapView.addSubview(pinView)
mapView.layer.insertSublayer(ellipsisLayer, below: pinView.layer)
let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(MapViewController.tappedDone(_:)))
button.title = "Done"
navigationItem.rightBarButtonItem = button
if let value = row.value {
let region = MKCoordinateRegion(center: value.coordinate, latitudinalMeters: 400, longitudinalMeters: 400)
mapView.setRegion(region, animated: true)
}
else{
mapView.showsUserLocation = true
}
updateTitle()
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let center = mapView.convert(mapView.centerCoordinate, toPointTo: pinView)
pinView.center = CGPoint(x: center.x, y: center.y - (pinView.bounds.height/2))
ellipsisLayer.position = center
}
#objc func tappedDone(_ sender: UIBarButtonItem){
let target = mapView.convert(ellipsisLayer.position, toCoordinateFrom: mapView)
row.value = CLLocation(latitude: target.latitude, longitude: target.longitude)
onDismissCallback?(self)
}
func updateTitle(){
let fmt = NumberFormatter()
fmt.maximumFractionDigits = 4
fmt.minimumFractionDigits = 4
let latitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.latitude))!
let longitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.longitude))!
title = "\(latitude), \(longitude)"
}
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
ellipsisLayer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
UIView.animate(withDuration: 0.2, animations: { [weak self] in
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y - 10)
})
}
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
ellipsisLayer.transform = CATransform3DIdentity
UIView.animate(withDuration: 0.2, animations: { [weak self] in
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y + 10)
})
updateTitle()
}
}
In my form, I just added a new row like this:
<<< LocationRow("Location"){
$0.title = $0.tag
$0.value = CLLocation(latitude: -34.9124, longitude: -56.1594)
}
This is also a good resource to use.
https://fluttergeek.com/blog/eureka-locationrow/

Display 2 view controllers at the same time with animation

I'm following this awesome video to create a custom transition for my project, because I'm developing for the iPad, so instead of presenting destination view controller full screen, I want to have it occupy half of the screen like this:
My code of the custom transition class is:
class CircularTransition: NSObject {
var circle = UIView()
var startingPoint = CGPoint.zero {
didSet {
circle.center = startingPoint
}
}
var circleColor = UIColor.white
var duration = 0.4
enum circularTransitionMode: Int {
case present, dismiss
}
var transitionMode = circularTransitionMode.present
}
extension CircularTransition: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
if transitionMode == .present {
if let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) {
var viewCenter = presentedView.center
var viewSize = presentedView.frame.size
if UIDevice.current.userInterfaceIdiom == .pad {
viewCenter = CGPoint(x: viewCenter.x, y: viewSize.height)
viewSize = CGSize(width: viewSize.width, height: viewSize.height)
}
circle = UIView()
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.width / 2
circle.center = startingPoint
circle.backgroundColor = circleColor
circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
containerView.addSubview(circle)
presentedView.center = startingPoint
presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
presentedView.alpha = 0
containerView.addSubview(presentedView)
UIView.animate(withDuration: duration, animations: {
self.circle.transform = CGAffineTransform.identity
presentedView.transform = CGAffineTransform.identity
presentedView.alpha = 1
presentedView.center = viewCenter
}, completion: {(sucess: Bool) in transitionContext.completeTransition(sucess)})
}
} else {
if let returningView = transitionContext.view(forKey: UITransitionContextViewKey.from) {
let viewCenter = returningView.center
let viewSize = returningView.frame.size
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.width / 2
circle.center = startingPoint
UIView.animate(withDuration: duration + 0.1, animations: {
self.circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.center = self.startingPoint
returningView.alpha = 0
}, completion: {(success: Bool) in
returningView.center = viewCenter
returningView.removeFromSuperview()
self.circle.removeFromSuperview()
transitionContext.completeTransition(success)
})
}
}
}
func frameForCircle(withViewCenter viewCenter: CGPoint, size viewSize: CGSize, startPoint: CGPoint) -> CGRect {
let xLength = fmax(startingPoint.x, viewSize.width - startingPoint.x)
let yLength = fmax(startingPoint.y, viewSize.height - startingPoint.y)
let offsetVector = sqrt(xLength * xLength + yLength * yLength) * 2
let size = CGSize(width: offsetVector, height: offsetVector)
return CGRect(origin: CGPoint.zero, size: size)
}
}
And the part of code in my view controller:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! ResultViewController
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = .custom
}
// MARK: - Animation
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transtion.transitionMode = .dismiss
transtion.startingPoint = calculateButton.center
transtion.circleColor = calculateButton.backgroundColor!
return transtion
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transtion.transitionMode = .present
transtion.startingPoint = calculateButton.center
transtion.circleColor = calculateButton.backgroundColor!
return transtion
}
But the controller shows up full screen.
You may try the two different Container View for half of top and bottom.
then give animation on it...
So I have finished creating my answer, It takes a different approach than the other answers so bear with me.
Instead of adding a container view what I figured would be the best way was to create a UIViewController subclass (which I called CircleDisplayViewController). Then all your VCs that need to have this functionality could inherit from it (rather than from UIViewController).
This way all your logic for presenting and dismissing ResultViewController is handled in one place and can be used anywhere in your app.
The way your VCs can use it is like so:
class AnyViewController: CircleDisplayViewController {
/* Only inherit from CircleDisplayViewController,
otherwise you inherit from UIViewController twice */
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func showCircle(_ sender: UIButton) {
openCircle(withCenter: sender.center, radius: nil, resultDataSource: calculator!.iterateWPItems())
//I'll get to this stuff in just a minute
//Edit: from talking to Bright Future in chat I saw that resultViewController needs to be setup with calculator!.iterateWPItems()
}
}
Where showCircle will present your ResultViewController using the transitioning delegate with the circle center at the sending UIButtons center.
The CircleDisplayViewController subclass is this:
class CircleDisplayViewController: UIViewController, UIViewControllerTransitioningDelegate, ResultDelegate {
private enum CircleState {
case collapsed, visible
}
private var circleState: CircleState = .collapsed
private var resultViewController: ResultViewController!
private lazy var transition = CircularTransition()
func openCircle(withCenter center: CGPoint, radius: CGFloat?, resultDataSource: ([Items], Int, String)) {
let circleCollapsed = (circleState == .collapsed)
DispatchQueue.main.async { () -> Void in
if circleCollapsed {
self.addCircle(withCenter: center, radius: radius, resultDataSource: resultDataSource)
}
}
}
private func addCircle(withCenter circleCenter: CGPoint, radius: CGFloat?, resultDataSource: ([Items], Int, String])) {
var circleRadius: CGFloat!
if radius == nil {
circleRadius = view.frame.size.height/2.0
} else {
circleRadius = radius
}
//instantiate resultViewController here, and setup delegate etc.
resultViewController = UIStoryboard.resultViewController()
resultViewController.transitioningDelegate = self
resultViewController.delegate = self
resultViewController.modalPresentationStyle = .custom
//setup any values for resultViewController here
resultViewController.dataSource = resultDataSource
//then set the frame of resultViewController (while also setting endFrame)
let resultOrigin = CGPoint(x: 0.0, y: circleCenter.y - circleRadius)
let resultSize = CGSize(width: view.frame.size.width, height: (view.frame.size.height - circleCenter.y) + circleRadius)
resultViewController.view.frame = CGRect(origin: resultOrigin, size: resultSize)
resultViewController.endframe = CGRect(origin: resultOrigin, size: resultSize)
transition.circle = UIView()
transition.startingPoint = circleCenter
transition.radius = circleRadius
transition.circle.frame = circleFrame(radius: transition.radius, center: transition.startingPoint)
present(resultViewController, animated: true, completion: nil)
}
func collapseCircle() { //THIS IS THE RESULT DELEGATE FUNCTIONS
dismiss(animated: true) {
self.resultViewController = nil
}
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .dismiss
transition.circleColor = UIColor.red
return transition
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .present
transition.circleColor = UIColor.red
return transition
}
func circleFrame(radius: CGFloat, center: CGPoint) -> CGRect {
let circleOrigin = CGPoint(x: center.x - radius, y: center.y - radius)
let circleSize = CGSize(width: radius*2, height: radius*2)
return CGRect(origin: circleOrigin, size: circleSize)
}
}
public extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: Bundle.main) }
}
private extension UIStoryboard {
class func resultViewController() -> ResultViewController {
return mainStoryboard().instantiateViewController(withIdentifier: "/* Your ID for ResultViewController */") as! ResultViewController
}
}
The only function that is called by the VCs that inherit from DisplayCircleViewController is openCircle, openCircle has a circleCenter argument (which should be your button center I'm guessing), an optional radius argument (if this is nil then a default value of half the view height is taken, and then whatever else you need to setup ResultViewController.
In the addCircle func there is some important stuff:
you setup ResultViewController however you have to before presenting (like you would in prepare for segue),
then setup the frame for it (I tried to make it the area of the circle that is visible but it is quite rough here, might be worth playing around with),
then this is where I reset the transition circle (rather than in the transition class), so that I could set the circle starting point, radius and frame here.
then just a normal present.
If you haven't set an identifier for ResultViewController you need to for this (see the UIStoryboard extensions)
I also changed the TransitioningDelegate functions so you don't set the circle center, this is because to keep it generic I put that responsibility to the ViewController that inherits from this one. (see top bit of code)
Finally I changed the CircularTransition class
I added a variable:
var radius: CGFloat = 0.0 //set in the addCircle function above
and changed animateTransition:
(removed the commented out lines):
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
if transitionMode == .present {
if let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) {
...
// circle = UIView()
// circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = radius
...
}
} else {
if let returningView = transitionContext.view(forKey: UITransitionContextViewKey.from) {
...
// circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
...
}
}
}
Finally I made a protocol so that ResultViewController could dismiss the circle
protocol ResultDelegate: class {
func collapseCircle()
}
class ResultViewController: UIViewController {
weak var delegate: ResultDelegate!
var endFrame: CGRect!
var dataSource: ([Items], Int, String)! // same as in Bright Future's case
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLayoutSubviews() {
if endFrame != nil {
view.frame = endFrame
}
}
#IBAction func closeResult(_ sender: UIButton) {
delegate.collapseCircle()
}
}
This has turned out to be quite a huge answer, sorry about that, I wrote it in a bit a of rush so if anything is not clear just say.
Hope this helps!
Edit: I found the problem, iOS 10 has changed the way they layout views, so to fix this I added an endFrame property to ResultViewController and set it's views frame to that in viewDidLayoutSubviews. I also set both the frame and endFrame at the same time in addCircle. I changed the code above to reflect the changes. It's not ideal but I'll have another look later to see if there is a better fix.
Edit: this is what it looks like open for me
Thanks to everyone for the suggestions, I tried to use a container view, here's how I did it:
First I added a containerView property in CircularTransition class:
class CircularTransition: NSObject {
...
var containerView: UIView
init(containerView: UIView) {
self.containerView = containerView
}
...
}
Then commented out these code in its extension:
// let containerView = transitionContext.containerView
// if UIDevice.current.userInterfaceIdiom == .pad {
// viewCenter = CGPoint(x: viewCenter.x, y: viewSize.height)
// viewSize = CGSize(width: viewSize.width, height: viewSize.height)
// }
In my mainViewController, I added a method to add a container view:
func addContainerView() {
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
containerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
])
transtion.containerView = containerView
}
The reason I don't use story board is, if I put the animated view controller (ResultViewController) in the container view, it gets loaded whenever mainViewController is loaded, however, ResultViewController needs the data from prepareForSegue, thus it'll crash.
Then I changed a little bit in prepareForSegue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
transtion.containerView = view
if UIDevice.current.userInterfaceIdiom == .pad {
addContainerView()
}
let secondVC = segue.destination as! ResultViewController
secondVC.transitioningDelegate = self
secondVC.modalPresentationStyle = .custom
secondVC.dataSource = calculator!.iterateWPItems().0
}
And created CircularTransition class this way in mainViewController:
let transtion = CircularTransition(containerView: UIView())
That's basically all I did, I could display the gorgeous dual vc view
on the iPad, however, the return transition doesn't work, I still
haven't figured out what caused that.
Hi i did some changes in your animateTransition method try this out. You might have to play a little bit with withRelativeStartTime of the animations and the frame and center to perfect the animation. But i guess this should get you started.
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
if transitionMode == .present {
if let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) {
var viewCenter = presentedView.center
var viewSize = presentedView.frame.size
if UIDevice.current.userInterfaceIdiom == .pad {
viewCenter = CGPoint(x: viewCenter.x, y: viewSize.height)
viewSize = CGSize(width: viewSize.width, height: viewSize.height)
}
circle = UIView()
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.width / 2
circle.center = startingPoint
circle.backgroundColor = circleColor
circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
circle.layer.masksToBounds = true
containerView.addSubview(circle)
presentedView.center = startingPoint
presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
presentedView.alpha = 0
containerView.addSubview(presentedView)
UIView.animateKeyframes(withDuration: duration, delay: 0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1, animations: {
self.circle.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
presentedView.alpha = 1
})
UIView.addKeyframe(withRelativeStartTime: 0.19, relativeDuration: 1, animations: {
presentedView.transform = CGAffineTransform(scaleX: 1, y: 1)
presentedView.frame = CGRect(x: 0, y: (containerView.frame.size.height / 2)+10, width: containerView.frame.size.width, height: containerView.frame.size.height*0.5)
})
}, completion: { (sucess) in
transitionContext.completeTransition(sucess)
})
}
} else {
if let returningView = transitionContext.view(forKey: UITransitionContextViewKey.from) {
let viewCenter = returningView.center
let viewSize = returningView.frame.size
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.width / 2
circle.center = startingPoint
UIView.animate(withDuration: duration + 0.1, animations: {
self.circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.center = self.startingPoint
returningView.alpha = 0
}, completion: {(success: Bool) in
returningView.center = viewCenter
returningView.removeFromSuperview()
self.circle.removeFromSuperview()
transitionContext.completeTransition(success)
})
}
}
}
Hope this helps.

UIView split transition

There is a good chance that I am just using the wrong terminology for this, but I have been looking to see if there is an iOS UIView transition that splits a view (BLUE) and any subview controls to reveal another view (RED) and its controls. I have found a couple of posts that mention something similar from 2011 but nothing recent, so was wondering if anything new had been added now we are upto iOS 8. Any pointers would be much appreciated.
If you are trying to do such split transition, I have created a animation controller for view controller transition. If you look at the code, you will find that there can be two different ways to transit, opening from view from the middle or the toview comes and collapses on the top of the from view.
Here is a small gif of how the code below works;
class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
let presenting: Bool
init(presenting: Bool) {
self.presenting = presenting
}
func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
return 1.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
animateOutImagesWithContext(transitionContext)
//animateInImagesWithContext(transitionContext)
}
func snapshotView(view: UIView!) -> UIImage {
UIGraphicsBeginImageContext(view.bounds.size)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshotImage
}
func animateOutImagesWithContext(transitionContext:UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let fromView = fromViewController!.view
let toView = toViewController!.view
containerView.addSubview(toView)
let snapshotImage = snapshotView(fromView)
fromView.removeFromSuperview()
let imageViews = animatingOutImageViews(snapshotImage)
containerView.addSubview(imageViews.firstImageView)
containerView.addSubview(imageViews.secondImageView)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
let firstImageView = imageViews.firstImageView
let secondImageView = imageViews.secondImageView
if self.presenting {
firstImageView.frame = CGRectOffset(firstImageView.frame, -CGRectGetWidth(firstImageView.frame), 0)
secondImageView.frame = CGRectOffset(secondImageView.frame, CGRectGetWidth(secondImageView.frame), 0)
} else {
firstImageView.frame = CGRectOffset(firstImageView.frame, 0, -CGRectGetHeight(firstImageView.frame))
secondImageView.frame = CGRectOffset(secondImageView.frame, 0, CGRectGetHeight(secondImageView.frame))
}
}) { (completed: Bool) -> Void in
imageViews.firstImageView.removeFromSuperview()
imageViews.secondImageView.removeFromSuperview()
transitionContext.completeTransition(true)
}
}
func animateInImagesWithContext(transitionContext:UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
let toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let fromView = fromViewController!.view
let toView = toViewController!.view
containerView.insertSubview(toView, belowSubview: fromView)
let snapshotImage = snapshotView(toView)
let imageViews = animatingInImageViews(snapshotImage)
containerView.addSubview(imageViews.firstImageView)
containerView.addSubview(imageViews.secondImageView)
UIView.animateWithDuration(transitionDuration(transitionContext), animations: { () -> Void in
let firstImageView = imageViews.firstImageView
let secondImageView = imageViews.secondImageView
if self.presenting {
firstImageView.frame = CGRectOffset(firstImageView.frame, 0, CGRectGetHeight(firstImageView.frame))
secondImageView.frame = CGRectOffset(secondImageView.frame, 0, -CGRectGetHeight(secondImageView.frame))
} else {
firstImageView.frame = CGRectOffset(firstImageView.frame, CGRectGetWidth(firstImageView.frame), 0)
secondImageView.frame = CGRectOffset(secondImageView.frame, -CGRectGetWidth(secondImageView.frame),0)
}
}) { (completed: Bool) -> Void in
fromView.removeFromSuperview()
imageViews.firstImageView.removeFromSuperview()
imageViews.secondImageView.removeFromSuperview()
transitionContext.completeTransition(true)
}
}
func animatingOutImageViews(snapshotImage: UIImage) -> (firstImageView: UIImageView!, secondImageView: UIImageView!)
{
let imageSize = snapshotImage.size
var firstPartFrame: CGRect
var secondPartFrame: CGRect
if presenting {
firstPartFrame = CGRectMake(0, 0, imageSize.width * 0.5, imageSize.height)
secondPartFrame = CGRectOffset(firstPartFrame, CGRectGetWidth(firstPartFrame), 0)
} else {
firstPartFrame = CGRectMake(0, 0, imageSize.width, imageSize.height * 0.5)
secondPartFrame = CGRectOffset(firstPartFrame, 0, CGRectGetHeight(firstPartFrame))
}
let firstImage = getImage(snapshotImage, insideRect: firstPartFrame)
let secondImage = getImage(snapshotImage, insideRect: secondPartFrame)
let firstImageView = UIImageView(frame: firstPartFrame)
firstImageView.image = firstImage
let secondImageView = UIImageView(frame: secondPartFrame)
secondImageView.image = secondImage
return (firstImageView, secondImageView)
}
func animatingInImageViews(snapshotImage: UIImage) -> (firstImageView: UIImageView!, secondImageView: UIImageView!)
{
let imageSize = snapshotImage.size
var firstPartFrame: CGRect
var secondPartFrame: CGRect
if presenting {
firstPartFrame = CGRectMake(0, 0, imageSize.width, imageSize.height * 0.5)
secondPartFrame = CGRectOffset(firstPartFrame, 0, CGRectGetHeight(firstPartFrame))
} else {
firstPartFrame = CGRectMake(0, 0, imageSize.width * 0.5, imageSize.height)
secondPartFrame = CGRectOffset(firstPartFrame, CGRectGetWidth(firstPartFrame), 0)
}
let firstImage = getImage(snapshotImage, insideRect: firstPartFrame)
let secondImage = getImage(snapshotImage, insideRect: secondPartFrame)
let firstImageView = UIImageView(image: firstImage)
let secondImageView = UIImageView(image: secondImage)
if presenting {
firstImageView.frame = CGRectOffset(firstPartFrame, 0, -CGRectGetHeight(firstPartFrame))
secondImageView.frame = CGRectOffset(secondPartFrame, 0, CGRectGetHeight(secondPartFrame))
} else {
firstImageView.frame = CGRectOffset(firstPartFrame, -CGRectGetWidth(firstPartFrame), 0)
secondImageView.frame = CGRectOffset(secondPartFrame, CGRectGetWidth(secondPartFrame), 0)
}
return (firstImageView, secondImageView)
}
func getImage(image: UIImage, insideRect rect:CGRect) -> UIImage {
let image = CGImageCreateWithImageInRect(image.CGImage, rect)!
return UIImage(CGImage: image)!
}
}
class SecondViewController: UIViewController, UIViewControllerTransitioningDelegate {
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
super.init(nibName: nil, bundle: nil)
self.transitioningDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
let view1 = UIView(frame: CGRectZero)
view1.backgroundColor = UIColor.purpleColor()
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
let view2 = UIView(frame: CGRectZero)
view2.backgroundColor = UIColor.cyanColor()
view2.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(view1)
view.addSubview(view2)
let views = [
"view1": view1,
"view2": view2
]
let vFormat = "V:|[view1][view2(==view1)]|"
let hFormat = "H:|[view1]|"
let hConstraints = NSLayoutConstraint.constraintsWithVisualFormat(hFormat,
options: .allZeros,
metrics: nil,
views: views)
let vConstraints = NSLayoutConstraint.constraintsWithVisualFormat(vFormat,
options: .AlignAllLeft | .AlignAllRight,
metrics: nil,
views: views)
view.addConstraints(hConstraints)
view.addConstraints(vConstraints)
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
action: "tapped")
view.addGestureRecognizer(tapGestureRecognizer)
}
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimationController(presenting: true)
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return AnimationController(presenting: false)
}
func tapped() {
let nextViewController = NextViewController()
dismissViewControllerAnimated(true, completion: nil)
}
}
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let view1 = UIView(frame: CGRectZero)
view1.backgroundColor = UIColor.redColor()
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
let view2 = UIView(frame: CGRectZero)
view2.backgroundColor = UIColor.greenColor()
view2.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(view1)
view.addSubview(view2)
let views = [
"view1": view1,
"view2": view2
]
let hFormat = "H:|[view1][view2(==view1)]|"
let vFormat = "V:|[view1]|"
let hConstraints = NSLayoutConstraint.constraintsWithVisualFormat(hFormat,
options: .AlignAllTop | .AlignAllBottom,
metrics: nil,
views: views)
let vConstraints = NSLayoutConstraint.constraintsWithVisualFormat(vFormat,
options: .allZeros,
metrics: nil,
views: views)
view.addConstraints(hConstraints)
view.addConstraints(vConstraints)
let tapGestureRecognizer = UITapGestureRecognizer(target: self,
action: "tapped")
view.addGestureRecognizer(tapGestureRecognizer)
}
func tapped() {
let secondViewController = SecondViewController()
presentViewController(secondViewController, animated: true, completion: nil)
}
}
The code might be little longer but has to be easy to understand. Tune up a little if you want.
There is no such built in transition in iOS. You have to make it yourself. One way to do this would be by using the UIViewControllerAnimatedTransitioning protocol, and doing a custom presentation. The animation steps that would be carried out by the custom animator object would be something like this,
1) create two half images of the blue view, and add them to the transitionContext's containerView (you can't split a UIView in half, so you need to use an image of it instead)
2) add the red controller's view to the transitionContext's containerView underneath the half images.
3) remove the blue view
4) slide the two half images off the screen by animating their constraint constant values.
You could use something like:
UIView *redView = [[UIView alloc]initWithFrame:CGRectMake(self.view.frame.size.width / 2, 0, 1, self.view.frame.size.height)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
[UIView animateWithDuration:1 animations:^{
redView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
} completion:^(BOOL finished) {
UIViewController2 *viewController = [UIViewController2 new];
[self presentViewController:viewController animated:NO completion:^{
[redView removeFromSuperview];
}];
}];
on button click.

Resources