Override show segue ( transmition left to right ) - xcode

I want to override the Show Segue , And change it to be from left to right .
I tried to make a custom segue and it worked pretty well but without the navigation bar
So , I need a segue from left to right and a navigation bar with button on the top right (to go back) .
My Code
import UIKit
class SegueFromLeft: UIStoryboardSegue
{
override func perform()
{
let src = self.sourceViewController
let dst = self.destinationViewController
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransformMakeTranslation(-src.view.frame.size.width, 0)
UIView.animateWithDuration(0.25,
delay: 0.0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: {
dst.view.transform = CGAffineTransformMakeTranslation(0, 0)
},
completion: { finished in
src.presentViewController(dst, animated: false, completion: nil)
}
)
}
}

Related

UIViewControllerTransitioningDelegate presenting doesn't work (though dismissing does)

I am trying to implement a custom appearing/disappearing animation for a modal UIViewController in my app.
I have published the code showing this error here.
Here is the content related:
/// The view controller from which I'm trying to display the modal
class ViewController: UIViewController {
#IBAction func tapped() {
/// The modal showing
ModalTestViewController.show()
}
//...
}
/// The displayed modal
open class ModalTestViewController: TransitioningModalViewController {
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
// 1. when I put transitioningDelegate here, case 1
}
public required init?(coder: NSCoder) {
fatalError()
}
open override func viewDidLoad() {
super.viewDidLoad()
// 2. if I put transitioningDelegate here, case 2
}
#IBAction func tapped() {
// a tap on the overlayView of my modal
dismiss(animated: true)
}
static func show() {
let modal = ModalTestViewController()
modal.modalPresentationStyle = .overCurrentContext
DispatchQueue.main.async {
UIApplication.shared.delegate?.window??.rootViewController?.present(modal, animated: true)
}
}
}
/// The default modal view controller, which all modals in my app should inherit
open class TransitioningModalViewController: UIViewController {
// MARK: View Properties
#IBOutlet weak var overlayView: UIView!
}
extension TransitioningModalViewController: UIViewControllerTransitioningDelegate {
public func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerPresenter()
}
public func animationController(
forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return TransitioningModalViewControllerDismisser()
}
}
private final class TransitioningModalViewControllerPresenter: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
private final class TransitioningModalViewControllerDismisser: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(
using transitionContext: UIViewControllerContextTransitioning?
) -> TimeInterval {
return 0.5
}
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let fromViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.from
) as! TransitioningModalViewController
let duration = transitionDuration(using: transitionContext)
UIView.animate(
withDuration: duration
) {
fromViewController.overlayView.alpha = 0.0
} completion: { result in
transitionContext.completeTransition(result)
}
}
}
The idea behind this is that the modal appearance should not be the usual bottom-to-top animation, but instead the overlay view should go from hidden to an alpha of 0,65.
Case 1: when I put transitioningDelegate = self in init(), the animation is killed and nothing happens.
Case 2: when I put it into the viewDidLoad(), the appearing animation is the default bottom-to-top one, but the disappearing one is the expecting one (with the overlay view vanishing).
It looks like something is wrong with the initial transitioningDelegate setting but I can't find what.
Thank you for your help!
In your original code, you are setting the delegate here:
open override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
transitioningDelegate = self
}
However, .present(...) is called before viewDidAppear(...), so the controller is presented with default slide-up animation.
Setting the delegate in init() doesn't work, because we have override the default presentation process... and the presented controller's view is never added to the view hierarchy.
This "quick fix" should do the job...
First, in ModalTestViewController, move setting the delegate to init():
init() {
super.init(nibName: "ModalTestViewController", bundle: .main)
transitioningDelegate = self
}
then, in TransitioningModalViewControllerPresenter, add these lines before the animation:
func animateTransition(
using transitionContext: UIViewControllerContextTransitioning
) {
let toViewController: TransitioningModalViewController = transitionContext.viewController(
forKey: UITransitionContextViewControllerKey.to
) as! TransitioningModalViewController
// add these lines \/
// get the "from" view controller
let fromVC = transitionContext.viewController(forKey: .from)!
// get the "to" view controller's view
let toView = transitionContext.view(forKey: .to)!
// set the frame of the "to" view to the initialFrame (the current frame) of the "from" VC
toView.frame = transitionContext.initialFrame(for: fromVC)
// get the transition container view
let container = transitionContext.containerView
// add the "to" view to the view hierarchy
container.addSubview(toView)
// add these lines /\
let duration = transitionDuration(using: transitionContext)
toViewController.overlayView.alpha = 0.0
UIView.animate(
withDuration: duration
) {
toViewController.overlayView.alpha = 0.65
} completion: { result in
transitionContext.completeTransition(result)
}
}
Personally, to make this more flexible, I would get rid of your overlayView and set the alpha on the controller's view itself.

Implementing NSSegmentedControl in NSToolbar to control NSTabViewController

In my macOS application, I'm trying to replicate the Photos.app implementation of NSSegmentedControl in NSToolbar to control an NSTabViewController. For reference, here's what that looks like:
So, my approach was as follows:
Hide the default NSTabView header using the Interface Builder
Programmatically add an NSToolbar
Insert NSSegmentedControl as an NSToolbarItem.
Use a #selector to listen for changes to NSSegmentedControl.
Here's the current implementation:
class WindowController: NSWindowController, NSToolbarDelegate {
// MARK: - Identifiers
let mainToolbarIdentifier = NSToolbar.Identifier("MAIN_TOOLBAR")
let segmentedControlIdentifier = NSToolbarItem.Identifier("MAIN_TABBAR")
// MARK: - Properties
var tabBar: NSSegmentedControl? = NSSegmentedControl(labels: ["One", "Two"], trackingMode: NSSegmentedControl.SwitchTracking.selectOne, target: self, action: #selector(didSwitchTabs))
var toolbar: NSToolbar?
var tabBarController: NSTabViewController?
// MARK: - Life Cycle
override func windowDidLoad() {
super.windowDidLoad()
self.toolbar = NSToolbar(identifier: mainToolbarIdentifier)
self.toolbar?.allowsUserCustomization = false
self.toolbar?.delegate = self
self.tabBar?.setSelected(true, forSegment: 0)
self.tabBarController = self.window?.contentViewController as? NSTabViewController
self.tabBarController?.selectedTabViewItemIndex = 0
self.window?.toolbar = self.toolbar
}
// MARK: - NSToolbarDelegate
public func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
var toolbarItem: NSToolbarItem
switch itemIdentifier {
case segmentedControlIdentifier:
toolbarItem = NSToolbarItem(itemIdentifier: segmentedControlIdentifier)
toolbarItem.view = self.tabBar
case NSToolbarItem.Identifier.flexibleSpace:
toolbarItem = NSToolbarItem(itemIdentifier: itemIdentifier)
default:
fatalError()
}
return toolbarItem
}
public func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [segmentedControlIdentifier, NSToolbarItem.Identifier.flexibleSpace]
}
public func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [NSToolbarItem.Identifier.flexibleSpace, segmentedControlIdentifier, NSToolbarItem.Identifier.flexibleSpace]
}
// MARK: - Selectors
#objc func didSwitchTabs(sender: Any) {
let segmentedControl = sender as! NSSegmentedControl
if (segmentedControl.selectedSegment == 0) {
self.tabBarController?.selectedTabViewItemIndex = 0
} else if (segmentedControl.selectedSegment == 1) {
self.tabBarController?.selectedTabViewItemIndex = 1
}
}
}
And, here it is in action:
Now, I am new to macOS development and this feels like it's a very complicated and convoluted way of solving this problem. Is there an easier way I could achieve the same thing ? Perhaps somehow in Interface Builder ? What could be done to improve here ? What have I done wrong ?
Thanks for your time.
For anybody implementing NSSegmentedControl on the toolbar and it did not trigger IBAction, I got the same problem and pay my half-day to resolve this.
The problem is I connect my segmented with the NSWindowController class.
To fix this, create a subclass of NSWindow, set that class to base class of window on your storyboard, then create #IBOutlet #IBAction link to NSWindow. Remember, link it with NSWindowController will not work.

Where I should put code to customize Cancel button in UISearchBar?

I trying to change Cancel button color in UISearchBar implemented with UISearchController (iOS 8 and greater). This is a code I use:
if self.resultSearchController.active {
for subView in self.resultSearchController.searchBar.subviews {
for subsubView in subView.subviews {
if subsubView.isKindOfClass(UIButton) {
subsubView.tintColor = UIColor.whiteColor()
}
}
}
}
If I paste it in viewDidLoad, it doesn't work, cause I think Cancel button initialize only when SearchController becomes Active.
If I paste code in viewDidLayoutSubviews everything work great, but I'm not sure its a correct way.
So, where I should put this code in TableViewController?
Also, I don't understand, how I can receive notification in my TableViewController that SearchController becomes inactive. In other words where I should put code like this:
if self.resultSearchController.active == false {
//Do something
}
First you should insert delegate methods :-
class HomeViewController: UIViewController,UISearchResultsUpdating, UISearchBarDelegate {
var searchController: UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search here..."
searchController.searchBar.delegate = self
searchController.searchBar.sizeToFit()
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
searchController.searchBar.tintColor = UIColor.whiteColor()
}
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
}
then used delegate methods and change cancel button colors and thing what you want
You can try this in AppDelegate's didFinishLaunchWithOptions:.
UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).tintColor = UIColor.whiteColor()
PS: This is a generic method and would affect UIBarButtonItem in UISearchBar across app.
Swift 4.2, 4.0+ An answer is added here for a custom search bar that can be customized as below,
You can check the usage of SearchBar class.

AlertView button to specific ViewController

I am looking at how to do an AlertView that when the button "OK" is pressed it takes you to a specific View Controller called DiveNumberViewController.
I have the AlertView code done (see below) but can't figure out how to have the OK button to the DiveNumberViewController. Any Help is appreciated.
I am using Xcode 6.3 and Swift
var Alert:UIAlertView = UIAlertView(title: "Alert", message: "Hello", delegate: self, cancelButtonTitle: "OK")
Alert.show()
Try this instead:
//// MARK - UIAlertViewDelegate
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
//index of cancel button
if buttonIndex == 0
{
//add code if needed
}
//index of OK button
if buttonIndex == 1
{
//add code to go to your controller
var divenumberViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DiveNumberViewController") as! DiveNumberViewController
self.navigationController?.pushViewController(divenumberViewController, animated: true)
}
}
Also, check your storyboard to be sure to have your controller class and your storyboard id setup inside.
Custom class | class : DiveNumberViewController
Identity | Storyboard ID : DiveNumberViewController
example here:
You can do like this :
var alert:UIAlertView = UIAlertView(title: "Alert", message: "Hello", delegate: self, cancelButtonTitle: "Cancel", otherButtonTitles:"OK")
alert.show()
Or
var alert:UIAlertView = UIAlertView(title: "Alert", message: "Hello", delegate: self, cancelButtonTitle: "Cancel")
alert.addButtonWithTitle("OK")
alert.show()
Don't forget to add the delegate
MyController : UIViewcontroller, UIAlertViewDelegate
When the user will click on the buttons, the delegate will fire this function below. So add your code here to go to DiveNumberViewController
//// MARK - UIAlertViewDelegate
func alertView(alertView: UIAlertView, clickedButtonAtIndex buttonIndex: Int) {
//index of cancel button
if buttonIndex == 0
{
//add code if needed
}
//index of OK button
if buttonIndex == 1
{
//add code to go to your controller
}
}
}
If you use a lot of UIAlertView in a same controller, you can add a tag to each UIAlertView like that. It will allow to add a specific action depending of the UIAlertView which is clicked
alertView.tag = 999
For more information, take a look on Apple's doc :
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html

Blank/Black view when attempting to set delegate/data source

I am attempting to load a view from a .xib file as per this question:
Assign xib to the UIView in Swift
My setup is as follows:
StripView.xib — the .xib file from which I am loading the view, contains a single UIView
StripView.swift — the 'owner' of the UIView in StripView.xib, contains references/IBOutlets for each component in StripView.xib
StripViewController.swift — The view controller, which loads an instance of StripView and handles most of the logic/data source/delegate stuff.
This all worked fine in Xcode 6 Beta 4, however, upgrading to Xcode 6 Beta 5 (the latest) breaks. On app launch, I simply get a blank, black screen. All other parts of the app seem to work fine. This bug only occurs when I attempt to set the delegate or data source of a UIPickerView (owned by StripView) to the StripViewController.
Code snippet:
class StripViewController: SWRevealViewController, RSColorPickerViewDelegate, UIPickerViewDataSource, UIPickerViewDelegate, BLEDelegate, UIActionSheetDelegate, UITextFieldDelegate {
var stripView: StripView!
var colorPicker: RSColorPickerView!
var connectBtn:UIButton!
var connectionSpinner:UIActivityIndicatorView!
var tapRecognizer:UITapGestureRecognizer!
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// use UIView extension as per https://stackoverflow.com/questions/24370061/assign-xib-to-the-uiview-in-swift
stripView = UIView.loadFromNibNamed("StripView") as StripView
self.view.addSubview(stripView)
// nav bar setup //
self.title = "Strip \(self.tabBarController.selectedIndex+1)"
let leftNav:UIView = UIView(frame: CGRectMake(0,0,100,40))
self.connectBtn = UIButton(frame: CGRectMake(0,0,95,40))
connectBtn.frame = CGRectMake(0,0,connectBtn.bounds.size.width,connectBtn.bounds.size.height)
connectBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
connectBtn.setTitle("Connect", forState: UIControlState.Normal)
connectBtn.setTitleColor(UIColor(red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0), forState: UIControlState.Normal)
connectBtn.addTarget(self, action: "connectButtonPressed:", forControlEvents: UIControlEvents.TouchDown)
leftNav.addSubview(connectBtn)
self.connectionSpinner = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.Gray)
connectionSpinner.frame = CGRectMake(connectBtn.frame.minX + connectBtn.bounds.size.width, connectBtn.frame.minY + 10, connectionSpinner.bounds.size.width, connectionSpinner.bounds.size.height)
connectionSpinner.hidesWhenStopped = true
leftNav.addSubview(connectionSpinner)
let leftNavItem:UIBarButtonItem = UIBarButtonItem(customView: leftNav)
self.navigationItem.leftBarButtonItem = leftNavItem
// set up color picker
colorPicker = RSColorPickerView(frame: CGRectMake(20, 78, 160, 160))
self.view.addSubview(colorPicker)
colorPicker.delegate = self
// set up mode picker
// THESE APPEAR TO BE THE OFFENDING LINES —
// COMMENTING THEM OUT DISPLAYS THE VIEW AS EXPECTED
self.stripView.modePicker.dataSource = self;
self.stripView.modePicker.delegate = self;
// set up notifications
NSNotificationCenter.defaultCenter().addObserver(self, selector: "colorFieldDidChange:", name: "ColorFieldDidChangeNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
// set up UI and populate fields
self.stripView.setAnimSpeed()
// set up BLE
BLEManager.instance.ble.delegate = self
}
[...]
Once more, the offending lines appear to be:
self.stripView.modePicker.dataSource = self;
self.stripView.modePicker.delegate = self;
If I comment them out, all else displays as normal. I even attempted moving the delegate/data source into StripView.swift (not StripViewController), but I see the same result.
Here's the part of StripViewController that implements UIPickerViewDataSource and UIPickerViewDelegate methods:
func numberOfComponentsInPickerView(pickerView: UIPickerView!) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView!, numberOfRowsInComponent component: Int) -> Int {
return DataModel.instance.ModeNames.count
}
func pickerView(pickerView: UIPickerView!, titleForRow row: Int, forComponent component: Int) -> String! {
return DataModel.instance.ModeNames[row]
}
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int) {
// send mode to arduino
println("--- sending mode select command ---")
var buf:[UInt8] = [0x00, 0x00, 0x00, 0x00]
buf[0] = Modes.instance.SET_MODE
buf[1] = UInt8(DataModel.instance.ModeOpcodes[DataModel.instance.ModeNames[row]]!)
let data:NSData = NSData(bytes: buf, length: 4)
BLEManager.instance.ble.write(data)
}
Did something change in the way the Swift language handles delegates/data sources, or view loading from a XIB?
EDIT:
I've narrowed down the problem to the DataModel singleton I'm using as a datasource. That code (DataModel.swift) looks like so, implementing a basic singleton pattern as per https://github.com/hpique/SwiftSingleton :
class DataModel {
// setup singleton
class var instance: DataModel {
struct Static {
static let instance : DataModel = DataModel()
}
return Static.instance
}
// modes
// eventually may replace this with a more flexible data structure
// for now, we just need names
let ModeNames: Array<String> = ["Light All", "Rainbow"]
let ModeOpcodes: Dictionary<String, UInt8> = [DataModel.instance.ModeNames[0]: Modes.instance.LIGHT_ALL, DataModel.instance.ModeNames[1]: Modes.instance.RAINBOW];
}
Replacing instances of this with a variable defined in StripViewController results in things working fine. Something about it being a singleton seems to be throwing things off. Thoughts? Perhaps the singleton is not being instantiated correctly?
One thing easily overlooked is connecting the data source and delegate to the view controller itself. In addition to ctrl dragging an outlet, you need to ctrl drag the data source and delegate from the connections inspector to the small view controller icon on top of the view in the storyboard.

Resources