How to change the ViewControllers frame position inside the PageViewController? - ios

The Code I Have written for loading view controllers in Page View Controllers.
Programatically creating four view controllers and adding them to Page View Controller.
I have changed the view controllers frame position but still not changing in the app
let controller: UIViewController = UIViewController()
print(controller.view.frame)
controller.view.frame = CGRectMake(10, 20, self.view.frame.width/2, self.view.frame.height - 20)
print(controller.view.frame)
controller.view.backgroundColor = UIColor.blackColor()
let controller2: UIViewController = UIViewController()
controller2.view.backgroundColor = UIColor.redColor()
let controller3: UIViewController = UIViewController()
controller3.view.backgroundColor = UIColor.blackColor()
let controller4: UIViewController = UIViewController()
controller4.view.backgroundColor = UIColor.greenColor()
let p1 = controller
let p2 = controller2
let p3 = controller3
let p4 = controller4
myViewControllers = [p1,p2,p3,p4]
for index in 0 ..< myViewControllers.count {
NSLog("\(myViewControllers[index])")
}
let startingViewController = self.viewControllerAtIndex(0)
let viewControllers: NSArray = [startingViewController]
self.setViewControllers(viewControllers as? [UIViewController], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: {(done: Bool) in
})

Related

Search bar gets removed from the view hierarchy after custom transition

I have two view controllers embedded into a UINavigationController. The first view controller has a UISearchController set to its navigation item. Here is the full code where I configure the search controller:
private func configureSearchController() {
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.hidesNavigationBarDuringPresentation = false
//searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.tintColor = .white
searchController.searchBar.delegate = self
//White search text
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes = [NSAttributedStringKey.foregroundColor.rawValue: UIColor.white]
//White placeholder
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: "search bar placeholder"), attributes: [NSAttributedStringKey.foregroundColor: UIColor.white])
//searchController.searchBar.sizeToFit()
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
navigationController?.navigationBar.prefersLargeTitles = false
//
definesPresentationContext = true
}
I call this method from viewDidLoad.
As mentioned in the question title, I use a navigation controller custom transition. Here is the transition animator's full code.
class RevealViewControllerAnimator: NSObject, UIViewControllerAnimatedTransitioning {
private let animationDuration = 1.5
var operation: UINavigationControllerOperation = .push
var isShowing = true
private weak var storedContext: UIViewControllerContextTransitioning?
var snapshot: UIView?
private lazy var viewOnTopOfSnapshot: UIView? = {
let view = UIView()
view.frame = self.snapshot!.frame
if isShowing {
view.backgroundColor = .clear
} else {
view.backgroundColor = UIColor(white: 0.3, alpha: 0.4)
}
return view
}()
private var backgroundViewBackgroundDarkColor = UIColor(white: 0.2, alpha: 0.4)
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
storedContext = transitionContext
print ("OPERATION", operation.rawValue)
//If we are presenting a view controller
if isShowing {
let fromVC = transitionContext.viewController(forKey: .from) as! ViewController1
let toVC = transitionContext.viewController(forKey: .to) as! ViewController2
snapshot = UIApplication.shared.keyWindow?.snapshotView(afterScreenUpdates: false)
let containerView = transitionContext.containerView
//Adding a view on top of a snapshot and animating its bacground color
if let snapshot = snapshot, let viewOnTopOfSnapshot = viewOnTopOfSnapshot {
containerView.addSubview(self.snapshot!)
containerView.insertSubview(viewOnTopOfSnapshot, aboveSubview: snapshot)
UIView.animate(withDuration: animationDuration - 1.0, animations: {
viewOnTopOfSnapshot.backgroundColor = self.backgroundViewBackgroundDarkColor
}, completion: nil)
}
containerView.addSubview(toVC.view)
toVC.view.frame = transitionContext.finalFrame(for: toVC)
animate(toView: toVC.view, fromTriggerButton: fromVC.filterButton)
} else {
//If we are dismissing the view controller
let fromVC = transitionContext.viewController(forKey: .from) as! ViewController2
let toVC = transitionContext.viewController(forKey: .to) as! ViewController1
let containerView = transitionContext.containerView
//Animating the background color change to clear
if let viewOnTopOfSnapshot = viewOnTopOfSnapshot {
UIView.animate(withDuration: animationDuration, animations: {
viewOnTopOfSnapshot.backgroundColor = .clear
}, completion: {_ in
self.snapshot?.removeFromSuperview()
viewOnTopOfSnapshot.removeFromSuperview()
})
}
//containerView.addSubview(fromVC.view)
containerView.insertSubview(toVC.view!, belowSubview: snapshot!)
animateDismisss(fromView: fromVC.view, toTriggerButton: fromVC.saveButton)
}
}
//MARK: Animation for pushing
private func animate(toView: UIView, fromTriggerButton button: UIButton) {
let rect = CGRect(x: toView.frame.maxX, y: toView.frame.minY, width: button.frame.width, height: button.frame.height)
let circleMaskPathInitial = UIBezierPath(ovalIn: rect)
let fullHeight = toView.bounds.height
let extremePoint = CGPoint(x: button.center.x, y: button.center.y - fullHeight)
let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))
let circleMaskPathFinal = UIBezierPath(ovalIn: button.frame.insetBy(dx: -radius - 1000, dy: -radius - 1000))
let maskLayer = CAShapeLayer()
maskLayer.path = circleMaskPathFinal.cgPath
toView.layer.mask = maskLayer
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = circleMaskPathInitial.cgPath
maskLayerAnimation.toValue = circleMaskPathFinal.cgPath
maskLayerAnimation.duration = animationDuration
maskLayerAnimation.delegate = self
maskLayer.add(maskLayerAnimation, forKey: "path")
}
//MARK: Animation for pop (dismiss)
private func animateDismisss(fromView: UIView, toTriggerButton button: UIButton) {
let rect = CGRect(x: button.frame.origin.x, y: button.frame.midY, width: button.frame.width, height: button.frame.width)
let finalCircleMaskPath = UIBezierPath(ovalIn: rect)
let fullHeight = fromView.bounds.height
let extremePoint = CGPoint(x: button.center.x, y: button.center.y - fullHeight)
let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))
let initialCircleMaskPath = UIBezierPath(ovalIn: button.frame.insetBy(dx: -radius, dy: -radius))
let maskLayer = CAShapeLayer()
maskLayer.path = finalCircleMaskPath.cgPath
fromView.layer.mask = maskLayer
let maskLayerAnimation = CABasicAnimation(keyPath: "path")
maskLayerAnimation.fromValue = initialCircleMaskPath.cgPath
maskLayerAnimation.toValue = finalCircleMaskPath.cgPath
maskLayerAnimation.duration = 0.8
maskLayerAnimation.delegate = self
maskLayer.add(maskLayerAnimation, forKey: "path")
}
extension RevealFilterViewControllerAnimator : CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
if let context = storedContext {
storedContext?.completeTransition(!context.transitionWasCancelled)
} else {
storedContext = nil
}
}
}
So, in two words, I get a snapshot of the ViewController1, insert it to the containerView, and on top of it, insert another view which background color I change during the animation. When popping, I get rid of the snapshot and the view, and also insert the ViewController1's view to a containerView.
As I mentioned in the beginning of the question, I have a UISearchController with a search bar in the first view controller.
The problem is, after dismissing the ViewController2 the search controller gets removed from the hierarchy and I get a blank white space. Here is the demonstration for that:
When I print the UISearchController or the search bar on the console, I get the object information, however, as you can see, it disappears from the view hierarchy (In a view hierarchy debugger I can't find it).
Why is this happening, and how this could be solved?
Finally, I figured out what was causing the issue, and once I did, the solution was quite simple. So, the reason why that was happening was that in the ViewController2's viewDidLoad method I was hiding the navigation bar, but I never set it back when popping the view controller.
So, here is the code that I use for the navigation bar in the second view controller (my view controllers look a little bit different but the logic is the same):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: true)
}
Here is how the animation looking right now (I know, there are some rough edges here, but at least, the problem is solved).

How to present popover from tab bar item?

I want to present a ViewController as popover in Swift 4, but it presents the Viewcontroller normally, this is the code:
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let favViewController = TrialViewController()
let exhibtionViewController = TrialViewController()
let menuViewController = ttttViewController()
let notificationViewController = TrialViewController()
let profileViewController = ttttViewController()
favViewController.tabBarItem.title = "first"
exhibtionViewController.tabBarItem.title = "second"
menuViewController.tabBarItem.title = "third"
notificationViewController.tabBarItem.title = "forth"
profileViewController.tabBarItem.title = "fifth"
favViewController.tabBarItem.image = UIImage(named:"home25")
exhibtionViewController.tabBarItem.image = UIImage(named: "bag25")
menuViewController.tabBarItem.image = UIImage(named: "main_add_25")
notificationViewController.tabBarItem.image = UIImage(named: "notification25")
profileViewController.tabBarItem.image = UIImage(named: "man_man25")
let tabBarItemWidth = Int(self.tabBar.frame.size.width) / (self.tabBar.items?.count)!
let x = tabBarItemWidth * 3;
let newRect = CGRect(x: x, y: 0, width: tabBarItemWidth, height: Int(self.tabBar.frame.size.height))
print(newRect)
menuViewController.modalPresentationStyle = .popover
menuViewController.view.frame = newRect
menuViewController.preferredContentSize = CGSize(width: 150,height: 150)
if let popoverMenuViewController = menuViewController.popoverPresentationController {
popoverMenuViewController.permittedArrowDirections = .down
popoverMenuViewController.delegate = menuViewController as? UIPopoverPresentationControllerDelegate
popoverMenuViewController.sourceRect = newRect
popoverMenuViewController.sourceView = self.tabBar
present(menuViewController, animated: true, completion: nil)
}
viewControllers = [favViewController, exhibtionViewController, menuViewController, notificationViewController, profileViewController]
}
}
what is the problem with my code?
override func viewDidAppear(_ animated: Bool) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "bbb") as! ttttViewController
vc.modalPresentationStyle = .popover //presentation style
vc.preferredContentSize = CGSize(width: 150,height: 150)
vc.popoverPresentationController?.delegate = self as! UIPopoverPresentationControllerDelegate
vc.popoverPresentationController?.sourceView = view
vc.popoverPresentationController?.sourceRect = self.tabBar.frame
self.present(vc, animated: true, completion: nil)
}
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
return .none
}
demo is here popover

Last row on child view controller's table view cannot select

I'm trying to make a view container with table view. That's works fine, but if i select the last row, the method didSelectRowAt indexPath: cannot be triggered. This is my code:
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let controller = storyboard.instantiateViewController(withIdentifier: "pageViewController") as! PageViewController
addChildViewController(controller)
controller.customDelegate = self
controller.index = index
self.controller = controller
//Add Controllers
let clientInfoVC = storyboard.instantiateViewController(withIdentifier: "ClientInfo") as! ClientInfoViewController
clientInfoVC.client = self.client
let clientRequestsVC = storyboard.instantiateViewController(withIdentifier: "ClientRequests") as! ClientProductRequestViewController
clientRequestsVC.client = self.client
let clientDebtsVC = storyboard.instantiateViewController(withIdentifier: "ClientDebts") as! ClientDebtViewController
clientDebtsVC.client = self.client
controller.orderedViewControllers = [clientInfoVC,clientRequestsVC,clientDebtsVC]
controller.view.frame = CGRect(x: 0, y: 0, width: self.clientContainerView.frame.width, height: self.clientContainerView.frame.height)
self.clientContainerView.addSubview(controller.view)
controller.didMove(toParentViewController: self)
And this is my container view:
What am I doing wrong?
Please help me
I found the solution:
I needed to remove the controller's frame setting and thats it. So Just remove this line:
controller.view.frame = CGRect(x: 0, y: 0, width: self.clientContainerView.frame.width,
height: self.clientContainerView.frame.height)

Anchor a popover to the rect of a selected textRange in textView iOS

I'm trying to present a popover from a UIMenuItem which anchor point is the rect of a selected text in a textView. I have the following code:
func pickColor(sender: UIMenuItem) {
let range = noteTextView.selectedTextRange
let beginningOfSelection = noteTextView.caretRect(for: (range?.start)!)
let endOfSelection = noteTextView.caretRect(for: (range?.end)!)
let storyboard: UIStoryboard = UIStoryboard(name: "ColorPicker", bundle: nil)
let colorVC = storyboard.instantiateViewController(withIdentifier: "ColorPickerViewController") as UIViewController
colorVC.modalPresentationStyle = .popover
let popover: UIPopoverPresentationController = colorVC.popoverPresentationController!
popover.sourceView = noteTextView
popover.sourceRect = CGRect(x: (beginningOfSelection.origin.x + endOfSelection.origin.x)/2, y: (beginningOfSelection.origin.y + beginningOfSelection.size.height)/2, width: 0, height: 0)
present(colorVC, animated: true, completion: nil)
}
The app crashes in the line colorVC.modalPresentationStyle = .popover. Can someone tell me what's going on here? Thanks! :)

UItableviewcontroller as popup not showing data

I want to show UITableViewController as popup on button press. I am able to show popup using this code
let menuViewController = popTableViewController() //popTableViewController
menuViewController.modalPresentationStyle = .Popover
menuViewController.preferredContentSize = CGSizeMake(320, 132)
//menuViewController.tableView = FrontTable
let popoverMenuViewController = menuViewController.popoverPresentationController
popoverMenuViewController?.permittedArrowDirections = .Any
popoverMenuViewController?.delegate = self
popoverMenuViewController?.sourceView = sender
popoverMenuViewController?.sourceRect = CGRect(
x: 10,
y: 10,
width: 1,
height: 1)
presentViewController(
menuViewController,
animated: true,
completion: nil)
but it is showing blank table view in popup. I have also added the content in tableview class but it not showing any reflection on result.
Try adding below code in your UIViewController
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
or you can try below code to create popover
let mpopover = self.storyboard?.instantiateViewControllerWithIdentifier("breedPop") as! breedPopover
mpopover.delegate = self
self.citiesPopover = mpopover
citiesPopover!.modalPresentationStyle = .Popover
citiesPopover!.preferredContentSize = CGSizeMake(UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height/2)
let popoverPresentationViewController = citiesPopover!.popoverPresentationController
popoverPresentationViewController?.permittedArrowDirections = UIPopoverArrowDirection.Any
popoverPresentationViewController?.delegate = self
popoverPresentationViewController?.sourceView = breedTextField
popoverPresentationViewController?.sourceRect = CGRectMake(breedTextField.frame.width, breedTextField.frame.height*3, 0, 0)
presentViewController(citiesPopover!, animated: true, completion: nil)

Resources