UINavigationController의 back bar 버튼을 눌렀을 때 동작 실행


204

a의 뒤로 버튼을 UINavigationController눌렀을 때 액션을 실행해야합니다 (배열 비어 있음). 이 버튼으로 인해 ViewController스택 의 이전 버튼이 계속 나타납니다. 스위프트를 사용하여 어떻게 이것을 할 수 있습니까? 여기에 이미지 설명을 입력하십시오

답변:


152

하나의 옵션은 사용자 정의 뒤로 버튼을 구현하는 것입니다. viewDidLoad 메소드에 다음 코드를 추가해야합니다.

- (void) viewDidLoad {
    [super viewDidLoad];
    self.navigationItem.hidesBackButton = YES;
    UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle:@"Back" style:UIBarButtonItemStyleBordered target:self action:@selector(back:)];
    self.navigationItem.leftBarButtonItem = newBackButton;
}

- (void) back:(UIBarButtonItem *)sender {
    // Perform your custom actions
    // ...
    // Go back to the previous ViewController
    [self.navigationController popViewControllerAnimated:YES];
}

최신 정보:

Swift의 버전은 다음과 같습니다.

    override func viewDidLoad {
        super.viewDidLoad()
        self.navigationItem.hidesBackButton = true
        let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Bordered, target: self, action: "back:")
        self.navigationItem.leftBarButtonItem = newBackButton
    }

    func back(sender: UIBarButtonItem) {
        // Perform your custom actions
        // ...
        // Go back to the previous ViewController
        self.navigationController?.popViewControllerAnimated(true)
    }

업데이트 2 :

Swift 3 버전은 다음과 같습니다.

    override func viewDidLoad {
        super.viewDidLoad()
        self.navigationItem.hidesBackButton = true
        let newBackButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.plain, target: self, action: #selector(YourViewController.back(sender:)))
        self.navigationItem.leftBarButtonItem = newBackButton
    }

    func back(sender: UIBarButtonItem) {
        // Perform your custom actions
        // ...
        // Go back to the previous ViewController
        _ = navigationController?.popViewController(animated: true)
    }

2
이전 뷰 컨트롤러에는 나타나지 않습니다. 루트 뷰 컨트롤러에 나타납니다.
rocky

91
일반 뒤로 버튼과 같은 화살표를 어떻게 사용할 수 있습니까?
TomSawyer

@rocky back 함수에서 아래 줄을 시도해 볼 수 있습니다. [self.navigationController dismissViewControllerAnimated : YES complete : nil];
malajisi

2
@TomSawyer이를 위해 아래 답변을보십시오.
fr33g

7
기능을 대체하기 위해 시스템 단추를 대체하는 것은 좋은 방법이 아닙니다. 가장 좋은 방법은 아래 답변입니다! stackoverflow.com/a/27715660/2307276
dpizzuto

477

다른 답변에서 제안한대로 버튼을 사용자 정의 버튼으로 바꾸는 것은 기본 동작과 스타일을 잃어 버릴 수 있으므로 좋은 생각이 아닙니다.

또 다른 옵션 은 View Controller 에서 viewWillDisappear 메소드 를 구현하고 isMovingFromParentViewController 라는 특성을 확인하는 입니다. 해당 속성이 true 인 경우 View Controller가 제거 (팝업)되어 사라지고 있음을 의미합니다.

다음과 같이 보일 것입니다 :

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParentViewController {
        // Your code...
    }
}

신속한 4.2

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        // Your code...
    }
}

5
@gmogames 네, 그렇게 할 수 없습니다. 질문은 그것을 요구하지 않았습니다. 되돌아가는 동작을 중지하려면 실제로 버튼을 재정의해야한다고 생각합니다.
manecosta

13
대한 스위프트 3.1 :override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController { // Your code... } }
더그 아모스

23
viewWillDisappear(animated:)전화를 받으면 트리거됩니다. 이것은 당신이 원하는 것이 아닐 것입니다. 아마도 더 나은 사용willMove(toParentViewController:)
조 Susnick

스위프트 4에서 누락 : 대체 func viewWillDisappear ( 애니메이션 : Bool)
Javier Calatrava Llavería

1
이것이 정답입니다. 깨끗하고 간단합니다.
temp

60
override func willMove(toParent parent: UIViewController?)
{
    super.willMove(toParent: parent)
    if parent == nil
    {
        print("This VC is 'will' be popped. i.e. the back button was pressed.")
    }
}

2
Swift3 / iOS10에서 작동하지 않는 콘솔 인쇄는 '중첩 팝 애니메이션으로 인해 탐색 모음이 손상 될 수 있습니다'.
itsji10dra

1
전혀 부름을받지 못함
zulkarnain shah

3
새로운 VC로 전환 할 때뿐만 아니라 되돌아 갈 때도 호출됩니다.
Jose Ramirez

@JozemiteApps 의견에 따르면, 그것은 뷰 컨트롤러가 컨테이너 뷰 컨트롤러에서 추가되거나 제거되기 직전에 문서에 있습니다. .
nstein

2
이것이 정답입니다. 그리고 때 parent == nil우리는 움직이고 때 다시 받는 parent장면
실번 D 애쉬

32

나는 다음과 같이 이것을 달성 할 수 있었다 :

스위프트 3

override func didMoveToParentViewController(parent: UIViewController?) {
   super.didMoveToParentViewController(parent)

   if parent == nil {
      println("Back Button pressed.")
      delegate?.goingBack()
   }           
}

스위프트 4

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)

    if parent == nil {
        debugPrint("Back Button pressed.")
    }
}

맞춤 뒤로 버튼이 필요 없습니다.


환상적입니다. 오래된 말이지 만 여전히 최신 Swift와 작동합니다.
user3204765

이것은 다음보기 컨트롤러 (이것 이상)에서 풀릴 때 트리거되므로 (거짓 긍정적) 실제로 뒤로 버튼 누름 감지는 아닙니다.
user2878850 오전

이전 코드와 동일하게이 코드는 뒤로 단추의 활성화를 감지하지 않고 현재보기의 팝업을 감지합니다.
빌 미르

31

이 (빠른) 클래스를 만들어 뒤로 화살표를 포함하여 일반적인 것과 같은 뒤로 버튼을 만들었습니다. 일반 텍스트 또는 이미지가있는 버튼을 만들 수 있습니다.

용법

weak var weakSelf = self

// Assign back button with back arrow and text (exactly like default back button)
navigationItem.leftBarButtonItems = CustomBackButton.createWithText("YourBackButtonTitle", color: UIColor.yourColor(), target: weakSelf, action: #selector(YourViewController.tappedBackButton))

// Assign back button with back arrow and image
navigationItem.leftBarButtonItems = CustomBackButton.createWithImage(UIImage(named: "yourImageName")!, color: UIColor.yourColor(), target: weakSelf, action: #selector(YourViewController.tappedBackButton))

func tappedBackButton() {

    // Do your thing

    self.navigationController!.popViewControllerAnimated(true)
}

CustomBackButtonClass

(Sketch & Paintcode 플러그인으로 작성된 뒤로 화살표를 그리기위한 코드)

class CustomBackButton: NSObject {

    class func createWithText(text: String, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] {
        let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
        negativeSpacer.width = -8
        let backArrowImage = imageOfBackArrow(color: color)
        let backArrowButton = UIBarButtonItem(image: backArrowImage, style: UIBarButtonItemStyle.Plain, target: target, action: action)
        let backTextButton = UIBarButtonItem(title: text, style: UIBarButtonItemStyle.Plain , target: target, action: action)
        backTextButton.setTitlePositionAdjustment(UIOffset(horizontal: -12.0, vertical: 0.0), forBarMetrics: UIBarMetrics.Default)
        return [negativeSpacer, backArrowButton, backTextButton]
    }

    class func createWithImage(image: UIImage, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] {
        // recommended maximum image height 22 points (i.e. 22 @1x, 44 @2x, 66 @3x)
        let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
        negativeSpacer.width = -8
        let backArrowImageView = UIImageView(image: imageOfBackArrow(color: color))
        let backImageView = UIImageView(image: image)
        let customBarButton = UIButton(frame: CGRectMake(0,0,22 + backImageView.frame.width,22))
        backImageView.frame = CGRectMake(22, 0, backImageView.frame.width, backImageView.frame.height)
        customBarButton.addSubview(backArrowImageView)
        customBarButton.addSubview(backImageView)
        customBarButton.addTarget(target, action: action, forControlEvents: .TouchUpInside)
        return [negativeSpacer, UIBarButtonItem(customView: customBarButton)]
    }

    private class func drawBackArrow(frame frame: CGRect = CGRect(x: 0, y: 0, width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) {
        /// General Declarations
        let context = UIGraphicsGetCurrentContext()!

        /// Resize To Frame
        CGContextSaveGState(context)
        let resizedFrame = resizing.apply(rect: CGRect(x: 0, y: 0, width: 14, height: 22), target: frame)
        CGContextTranslateCTM(context, resizedFrame.minX, resizedFrame.minY)
        let resizedScale = CGSize(width: resizedFrame.width / 14, height: resizedFrame.height / 22)
        CGContextScaleCTM(context, resizedScale.width, resizedScale.height)

        /// Line
        let line = UIBezierPath()
        line.moveToPoint(CGPoint(x: 9, y: 9))
        line.addLineToPoint(CGPoint.zero)
        CGContextSaveGState(context)
        CGContextTranslateCTM(context, 3, 11)
        line.lineCapStyle = .Square
        line.lineWidth = 3
        color.setStroke()
        line.stroke()
        CGContextRestoreGState(context)

        /// Line Copy
        let lineCopy = UIBezierPath()
        lineCopy.moveToPoint(CGPoint(x: 9, y: 0))
        lineCopy.addLineToPoint(CGPoint(x: 0, y: 9))
        CGContextSaveGState(context)
        CGContextTranslateCTM(context, 3, 2)
        lineCopy.lineCapStyle = .Square
        lineCopy.lineWidth = 3
        color.setStroke()
        lineCopy.stroke()
        CGContextRestoreGState(context)

        CGContextRestoreGState(context)
    }

    private class func imageOfBackArrow(size size: CGSize = CGSize(width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) -> UIImage {
        var image: UIImage

        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        drawBackArrow(frame: CGRect(origin: CGPoint.zero, size: size), color: color, resizing: resizing)
        image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }

    private enum ResizingBehavior {
        case AspectFit /// The content is proportionally resized to fit into the target rectangle.
        case AspectFill /// The content is proportionally resized to completely fill the target rectangle.
        case Stretch /// The content is stretched to match the entire target rectangle.
        case Center /// The content is centered in the target rectangle, but it is NOT resized.

        func apply(rect rect: CGRect, target: CGRect) -> CGRect {
            if rect == target || target == CGRect.zero {
                return rect
            }

            var scales = CGSize.zero
            scales.width = abs(target.width / rect.width)
            scales.height = abs(target.height / rect.height)

            switch self {
                case .AspectFit:
                    scales.width = min(scales.width, scales.height)
                    scales.height = scales.width
                case .AspectFill:
                    scales.width = max(scales.width, scales.height)
                    scales.height = scales.width
                case .Stretch:
                    break
                case .Center:
                    scales.width = 1
                    scales.height = 1
            }

            var result = rect.standardized
            result.size.width *= scales.width
            result.size.height *= scales.height
            result.origin.x = target.minX + (target.width - result.width) / 2
            result.origin.y = target.minY + (target.height - result.height) / 2
            return result
        }
    }
}

스위프트 3.0

class CustomBackButton: NSObject {

    class func createWithText(text: String, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] {
        let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
        negativeSpacer.width = -8
        let backArrowImage = imageOfBackArrow(color: color)
        let backArrowButton = UIBarButtonItem(image: backArrowImage, style: UIBarButtonItemStyle.plain, target: target, action: action)
        let backTextButton = UIBarButtonItem(title: text, style: UIBarButtonItemStyle.plain , target: target, action: action)
        backTextButton.setTitlePositionAdjustment(UIOffset(horizontal: -12.0, vertical: 0.0), for: UIBarMetrics.default)
        return [negativeSpacer, backArrowButton, backTextButton]
    }

    class func createWithImage(image: UIImage, color: UIColor, target: AnyObject?, action: Selector) -> [UIBarButtonItem] {
        // recommended maximum image height 22 points (i.e. 22 @1x, 44 @2x, 66 @3x)
        let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.fixedSpace, target: nil, action: nil)
        negativeSpacer.width = -8
        let backArrowImageView = UIImageView(image: imageOfBackArrow(color: color))
        let backImageView = UIImageView(image: image)
        let customBarButton = UIButton(frame: CGRect(x: 0, y: 0, width: 22 + backImageView.frame.width, height: 22))
        backImageView.frame = CGRect(x: 22, y: 0, width: backImageView.frame.width, height: backImageView.frame.height)
        customBarButton.addSubview(backArrowImageView)
        customBarButton.addSubview(backImageView)
        customBarButton.addTarget(target, action: action, for: .touchUpInside)
        return [negativeSpacer, UIBarButtonItem(customView: customBarButton)]
    }

    private class func drawBackArrow(_ frame: CGRect = CGRect(x: 0, y: 0, width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) {
        /// General Declarations
        let context = UIGraphicsGetCurrentContext()!

        /// Resize To Frame
        context.saveGState()
        let resizedFrame = resizing.apply(CGRect(x: 0, y: 0, width: 14, height: 22), target: frame)
        context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
        let resizedScale = CGSize(width: resizedFrame.width / 14, height: resizedFrame.height / 22)
        context.scaleBy(x: resizedScale.width, y: resizedScale.height)

        /// Line
        let line = UIBezierPath()
        line.move(to: CGPoint(x: 9, y: 9))
        line.addLine(to: CGPoint.zero)
        context.saveGState()
        context.translateBy(x: 3, y: 11)
        line.lineCapStyle = .square
        line.lineWidth = 3
        color.setStroke()
        line.stroke()
        context.restoreGState()

        /// Line Copy
        let lineCopy = UIBezierPath()
        lineCopy.move(to: CGPoint(x: 9, y: 0))
        lineCopy.addLine(to: CGPoint(x: 0, y: 9))
        context.saveGState()
        context.translateBy(x: 3, y: 2)
        lineCopy.lineCapStyle = .square
        lineCopy.lineWidth = 3
        color.setStroke()
        lineCopy.stroke()
        context.restoreGState()

        context.restoreGState()
    }

    private class func imageOfBackArrow(_ size: CGSize = CGSize(width: 14, height: 22), color: UIColor = UIColor(hue: 0.59, saturation: 0.674, brightness: 0.886, alpha: 1), resizing: ResizingBehavior = .AspectFit) -> UIImage {
        var image: UIImage

        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        drawBackArrow(CGRect(origin: CGPoint.zero, size: size), color: color, resizing: resizing)
        image = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return image
    }

    private enum ResizingBehavior {
        case AspectFit /// The content is proportionally resized to fit into the target rectangle.
        case AspectFill /// The content is proportionally resized to completely fill the target rectangle.
        case Stretch /// The content is stretched to match the entire target rectangle.
        case Center /// The content is centered in the target rectangle, but it is NOT resized.

        func apply(_ rect: CGRect, target: CGRect) -> CGRect {
            if rect == target || target == CGRect.zero {
                return rect
            }

            var scales = CGSize.zero
            scales.width = abs(target.width / rect.width)
            scales.height = abs(target.height / rect.height)

            switch self {
            case .AspectFit:
                scales.width = min(scales.width, scales.height)
                scales.height = scales.width
            case .AspectFill:
                scales.width = max(scales.width, scales.height)
                scales.height = scales.width
            case .Stretch:
                break
            case .Center:
                scales.width = 1
                scales.height = 1
            }

            var result = rect.standardized
            result.size.width *= scales.width
            result.size.height *= scales.height
            result.origin.x = target.minX + (target.width - result.width) / 2
            result.origin.y = target.minY + (target.height - result.height) / 2
            return result
        }
    }
}

iOS 11에 대한 답변을 업데이트 하시겠습니까?
BR41N-FCK 2016

2
안녕하세요 @guido, 솔루션이 완벽합니다. 코드를 시도한 결과 음수 너비의 barbutton을 추가했지만 뒤로 버튼 앞에 공간이 있음을 알았습니다.
Pawriwes

26

뒤로 화살표가있는 뒤로 버튼을 원하면 아래 이미지와 코드를 사용할 수 있습니다

backArrow.png 화살표 1backArrow@2x.png 화살표 2backArrow@3x.png화살표 3

override func viewDidLoad() {
    super.viewDidLoad()
    let customBackButton = UIBarButtonItem(image: UIImage(named: "backArrow") , style: .plain, target: self, action: #selector(backAction(sender:)))
    customBackButton.imageInsets = UIEdgeInsets(top: 2, left: -8, bottom: 0, right: 0)
    navigationItem.leftBarButtonItem = customBackButton
}

func backAction(sender: UIBarButtonItem) {
    // custom actions here
    navigationController?.popViewController(animated: true)
}

12

당신이 사용하는 경우 navigationController다음 추가 UINavigationControllerDelegate클래스 프로토콜을 다음과 같이 대리자 메서드를 추가합니다 :

class ViewController:UINavigationControllerDelegate {

    func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController,
animated: Bool) {
        if viewController === self {
            // do here what you want
        }
    }
}

이 메소드는 탐색 컨트롤러가 새 화면으로 슬라이드 할 때마다 호출됩니다. 뒤로 버튼을 누르면 새 뷰 컨트롤러 ViewController자체 가 됩니다.


NSObjectProtocol이 아닌 클래스를 델리게이트로 사용할 때 끔찍한 것입니다.
Nick Weaver

뒤로 버튼을 누를 때 항상 호출되는 것은 아닙니다.
Ted

9

Swift 5 및 Xcode 10.2에서

맞춤 바 버튼 항목을 추가하지 말고이 기본 동작을 사용하십시오.

viewWillDisappear 필요 없음 , 사용자 정의 BarButtonItem 등 필요 없음 ...

VC가 부모에서 제거 된 시점을 감지하는 것이 좋습니다.

이 두 기능 중 하나를 사용하십시오

override func willMove(toParent parent: UIViewController?) {
    super.willMove(toParent: parent)
    if parent == nil {
        callStatusDelegate?.backButtonClicked()//Here write your code
    }
}

override func didMove(toParent parent: UIViewController?) {
    super.didMove(toParent: parent)
    if parent == nil {
        callStatusDelegate?.backButtonClicked()//Here write your code
    }
}

뒤로 버튼의 기본 동작을 중지하려면 사용자 정의 BarButtonItem을 추가하십시오.


1
프로그래밍 방식으로 팝업 할 때도 호출되며, 뒤로 버튼을 누르는 것만이 아닙니다.
Ixx

7

아니

override func willMove(toParentViewController parent: UIViewController?) { }

이것은 당신이 segueing 경우에도 호출되는 것 이 방법을 무시하는 뷰 컨트롤러. " parent"가 nil아닌지 확인 하는 것이 올바른 방법으로 되돌아 가는 정확한 방법이 아닙니다 UIViewController. 이 현재 제시된 항목으로 UINavigationController올바르게 탐색 하는지 정확하게 확인하려면 다음 UIViewController을 준수해야합니다.UINavigationControllerDelegate 프로토콜 합니다.

참고 : 되돌아가는 것을 감지하려는 MyViewController모든 이름입니다 UIViewController.

1) 파일 상단에 추가하십시오 UINavigationControllerDelegate.

class MyViewController: UIViewController, UINavigationControllerDelegate {

2) UIViewController당신이 강탈당하는 것을 추적 할 속성을 수업에 추가하십시오 .

class MyViewController: UIViewController, UINavigationControllerDelegate {

var previousViewController:UIViewController

3) MyViewControllerviewDidLoad방법 self에서 귀하의 대리인으로 지정 합니다 UINavigationController.

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.delegate = self
}

3) 확인 하기 전에이UIViewController 속성으로 이전 을 할당하십시오 .

// In previous UIViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "YourSegueID" {
        if let nextViewController = segue.destination as? MyViewController {
            nextViewController.previousViewController = self
        }
    }
}

4) 하나의 방법에 준거 MyViewControllerUINavigationControllerDelegate

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    if viewController == self.previousViewController {
        // You are going back
    }
}

1
유용한 답변에 감사드립니다! 독자들은 UINavigationController의 델리게이트를 특정 뷰 컨트롤러로 설정하는 것을주의해야합니다. 내비게이션 컨트롤러에 이미 델리게이트가있는 경우 다른 콜백 델리게이트가 기대하지 못하게 할 위험이 있습니다. 우리 앱에서 UINavigationController의 델리게이트는 모든 뷰 컨트롤러가 가리키는 공유 객체 (AppCoordinator)입니다.
Bill Feth

7

내 경우에는 viewWillDisappear가장 잘 작동했습니다. 그러나 경우에 따라 이전 뷰 컨트롤러를 수정해야합니다. 그래서 여기 이전보기 컨트롤러에 액세스 할 수있는 솔루션이 있으며 Swift 4 에서 작동합니다 .

override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        if isMovingFromParentViewController {
            if let viewControllers = self.navigationController?.viewControllers {
                if (viewControllers.count >= 1) {
                    let previousViewController = viewControllers[viewControllers.count-1] as! NameOfDestinationViewController
                    // whatever you want to do
                    previousViewController.callOrModifySomething()
                }
            }
        }
    }

-viewDidDisappear (또는 -viewWillDisappear)는 뷰가 <View 버튼을 눌렀을 때뿐만 아니라 다른 뷰 컨트롤러의 뷰로 덮여 있어도 호출되므로 isMovingFromParentViewController를 확인해야합니다.
Bill Feth

5

현재 컨트롤러를 떠나기 전에 경고를 표시해야합니다. 그래서 나는 이렇게했다 :

  1. UINavigationController와 확장을 추가UINavigationBarDelegate
  2. 컨트롤러 네비게이션에 셀렉터 추가 ShouldPopOnBack (완료 :)

작동했습니다)

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let items = navigationBar.items, viewControllers.count < items.count {
            return true
        }

        let clientInfoVC = topViewController as? ClientInfoVC
        if clientInfoVC?.responds(to: #selector(clientInfoVC?.navigationShouldPopOnBack)) ?? false {
            clientInfoVC?.navigationShouldPopOnBack(completion: { isAllowPop in
                if isAllowPop {
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                }
            })
        }

        DispatchQueue.main.async {
            self.popViewController(animated: true)
        }

        return false
    }
}

@objc func navigationShouldPopOnBack(completion: @escaping (Bool) -> ()) {
        let ok = UIAlertAction(title: R.string.alert.actionOk(), style: .default) { _ in
            completion(true)
        }
        let cancel = UIAlertAction(title: R.string.alert.actionCancel(), style: .cancel) { _ in
            completion(false)
        }
        let alertController = UIAlertController(title: "", message: R.string.alert.contractMessage(), preferredStyle: .alert)
        alertController.addAction(ok)
        alertController.addAction(cancel)
        present(alertController, animated: true, completion: nil)
    }

4

우리가하는 것처럼 어렵지 않습니다. 배경색이 명확한 UIButton 용 프레임을 만들고 버튼에 동작을 지정한 다음 탐색 모음 뒤로 버튼 위에 놓습니다. 마지막으로 사용 후 버튼을 제거하십시오.

다음은 UIButton 대신 UIImage로 수행 된 Swift 3 샘플 코드입니다.

override func viewDidLoad() {
    super.viewDidLoad()
    let imageView = UIImageView()
    imageView.backgroundColor = UIColor.clear
    imageView.frame = CGRect(x:0,y:0,width:2*(self.navigationController?.navigationBar.bounds.height)!,height:(self.navigationController?.navigationBar.bounds.height)!)
    let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(back(sender:)))
    imageView.isUserInteractionEnabled = true
    imageView.addGestureRecognizer(tapGestureRecognizer)
    imageView.tag = 1
    self.navigationController?.navigationBar.addSubview(imageView)
    }

코드를 작성해야합니다

func back(sender: UIBarButtonItem) {

    // Perform your custom actions}
    _ = self.navigationController?.popViewController(animated: true)

    }

조치가 수행 된 후 subView를 제거하십시오.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    for view in (self.navigationController?.navigationBar.subviews)!{
        if view.tag == 1 {
            view.removeFromSuperview()
        }
    }

고마워 친구 . :-)
ARSHWIN DENUEV LAL

터치 다운 할 때 상태를 어떻게 작성합니까?
quang thang

iOS 11에서는 작동하지 않는 것 같습니다. UIImageView의 배경색이 선명하지 않을 때는 아닙니다. 다른 색상으로 설정하면 작동합니다.
Forms

선명한 색상으로 UIImageView를 정의하고, 프레임을 설정하고, tapgesture를 할당하고, 화면 어디에나 배치 할 수 있습니다. 그런 다음 탐색 모음 위에 배치 할 수없는 이유는 무엇입니까? 진심으로 내가 쓴 것을 추천하지 않습니다. 문제가 있다면 분명히 이유가 있지만 색상이 중요하지 않습니다. 코드가 논리 u를 따르는 것을 잊어라. :)
ARSHWIN DENUEV LAL

4

스위프트 4.2 :

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        // Your code...

    }
}

3

스위프트 3 :

override func didMove(toParentViewController parent: UIViewController?) {
    super.didMove(toParentViewController: parent)

    if parent == nil{
        print("Back button was clicked")
    }
}

-did / willMove (toParentViewController :)는 -viewWillDisappear에서 isMovingTfromParentViewController를 확인하는 것보다보기 컨트롤러가 실제로 부모를 변경할 때만 호출되므로 호출되기 때문에 (보기가 다른 VC의 관점에서 다루지 않을 때) 더 나은 해결책 일 것입니다. UINavigationController 델리게이트 메소드를 구현합니다. 그러나 조심하십시오. NavigationController에 이미 델리게이트가있는 경우 다른 델리게이트 델리게이트가 예상하지 못하게 할 위험이 있습니다.
Bill Feth

splitViewController로 테스트했습니다. 거기에 추가 또는 제거 사이의 차이를 만들 수 없습니다.
claude31

2

이 시도 .

self.navigationItem.leftBarButtonItem?.target = "methodname"
func methodname ( ) {            
  //    enter code here
}

이것도 시도하십시오.

override func viewWillAppear(animated: Bool) {
  //empty your array
}

2

그냥 제어 + 항목을 func 아래로 드래그하십시오. 매력처럼 일하다

@IBAction func done(sender: AnyObject) {
    if((self.presentingViewController) != nil){
        self.dismiss(animated: false, completion: nil)
        print("done")
    }
}

여기에 이미지 설명을 입력하십시오


2

서브 클래 싱 UINavigationController및 재정의 할 수 있습니다 popViewController(animated: Bool). 일부 코드를 실행할 수있을뿐만 아니라 현재 작업을 저장하거나 삭제하라는 메시지와 같이 사용자가 다시 돌아 가지 못하게 할 수도 있습니다.

popHandler푸시 된 컨트롤러에 의해 설정 / 삭제되는을 설정할 수있는 샘플 구현 입니다.

class NavigationController: UINavigationController
{
    var popHandler: (() -> Bool)?

    override func popViewController(animated: Bool) -> UIViewController?
    {
        guard self.popHandler?() != false else
        {
            return nil
        }
        self.popHandler = nil
        return super.popViewController(animated: animated)
    }
}

저장되지 않은 작업을 추적하는 푸시 된 컨트롤러의 샘플 사용량.

let hasUnsavedWork: Bool = // ...
(self.navigationController as! NavigationController).popHandler = hasUnsavedWork ?
    {
        // Prompt saving work here with an alert

        return false // Prevent pop until as user choses to save or discard

    } : nil // No unsaved work, we clear popHandler to let it pop normally

interactivePopGestureRecognizer사용자가 스 와이프 제스처를 사용하여 되돌아 가려고 할 때도 좋은 터치 입니다.


우수 답변, 고맙습니다 Rivera
DvixExtract

2

이것은 나의 해결책이다

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        if let shouldBlock = self.topViewController?.shouldPopFromNavigation() {
            return shouldBlock
        }
        return true
    }
}

extension UIViewController {
    @objc func shouldPopFromNavigation() -> Bool {
        return true
    }
}

뷰 컨트롤러에서 다음과 같이 처리 할 수 ​​있습니다.

@objc override func shouldPopFromNavigation() -> Bool {
        // Your dialog, example UIAlertViewController or whatever you want
        return false
    }

1

난 당신이 빈하려는 알고있는 것처럼 array당신이 다시 버튼을 누르면로하고 이전에 팝업 ViewController let당신 Array이 화면은 당신이로드 한

let settingArray  = NSMutableArray()
@IBAction func Back(sender: AnyObject) {
    self. settingArray.removeAllObjects()
    self.dismissViewControllerAnimated(true, completion: nil)
} 

1
    override public func viewDidLoad() {
         super.viewDidLoad()
         self.navigationController?.navigationBar.topItem?.title = GlobalVariables.selectedMainIconName
         let image = UIImage(named: "back-btn")

         image = image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)

        self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: UIBarButtonItemStyle.Plain, target: self, action: #selector(Current[enter image description here][1]ViewController.back) )
    }

    func back() {
      self.navigationController?.popToViewController( self.navigationController!.viewControllers[ self.navigationController!.viewControllers.count - 2 ], animated: true)
    }

1
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingToParent {

        //your code backView
    }
}

1

들어 스위프트 5 , 우리는 사라집니다보기에서 확인하실 수 있습니다

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if self.isMovingFromParent {
        delegate?.passValue(clickedImage: selectedImage)
    }
}

1

스위프트 5 __ Xcode 11.5

제 경우에는 애니메이션을 만들고 싶었을 때 돌아갑니다. 뒤로 단추의 기본 조치를 겹쳐 쓰고 사용자 정의 조치를 호출하는 방법은 다음과 같습니다.

     override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        setBtnBack()
    }

    private func setBtnBack() {
        for vw in navigationController?.navigationBar.subviews ?? [] where "\(vw.classForCoder)" == "_UINavigationBarContentView" {
            print("\(vw.classForCoder)")
            for subVw in vw.subviews where "\(subVw.classForCoder)" == "_UIButtonBarButton" {
                let ctrl = subVw as! UIControl
                ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
                ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
            }
        }
    }


    @objc func backBarBtnAction() {
        doSomethingBeforeBack { [weak self](isEndedOk) in
            if isEndedOk {
                self?.navigationController?.popViewController(animated: true)
            }
        }
    }


    private func doSomethingBeforeBack(completion: @escaping (_ isEndedOk:Bool)->Void ) {
        UIView.animate(withDuration: 0.25, animations: { [weak self] in
            self?.vwTxt.alpha = 0
        }) { (isEnded) in
            completion(isEnded)
        }
    }

NavigationBar보기 계층

또는이 메소드를 한 번 사용하여 NavigationBar보기 계층 구조를 탐색하고 인덱스가 _UIButtonBarButton보기에 액세스하고 UIControl로 캐스트하고 대상 조치를 제거하고 사용자 정의 대상 조치를 추가 할 수 있습니다.

    private func debug_printSubviews(arrSubviews:[UIView]?, level:Int) {
        for (i,subVw) in (arrSubviews ?? []).enumerated() {
            var str = ""
            for _ in 0...level {
                str += "\t"
            }
            str += String(format: "%2d %@",i, "\(subVw.classForCoder)")
            print(str)
            debug_printSubviews(arrSubviews: subVw.subviews, level: level + 1)
        }
    }

    // Set directly the indexs
    private func setBtnBack_method2() {
        // Remove or comment the print lines
        debug_printSubviews(arrSubviews: navigationController?.navigationBar.subviews, level: 0)   
        let ctrl = navigationController?.navigationBar.subviews[1].subviews[0] as! UIControl
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.removeTarget(ctrl.allTargets.first, action: nil, for: .allEvents)
        print("ctrl.allTargets: \(ctrl.allTargets)")
        ctrl.addTarget(self, action: #selector(backBarBtnAction), for: .touchUpInside)
        print("ctrl.allTargets: \(ctrl.allTargets)")
    }

0

나는 이것을 호출 / 재정의 viewWillDisappear한 후 다음 navigationController과 같은 스택에 액세스하여 이것을 달성 했습니다.

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    let stack = self.navigationController?.viewControllers.count

    if stack >= 2 {
        // for whatever reason, the last item on the stack is the TaskBuilderViewController (not self), so we only use -1 to access it
        if let lastitem = self.navigationController?.viewControllers[stack! - 1] as? theViewControllerYoureTryingToAccess {
            // hand over the data via public property or call a public method of theViewControllerYoureTryingToAccess, like
            lastitem.emptyArray()
            lastitem.value = 5
        }
    }
}

0

이것이 내 문제를 해결하는 방법입니다.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationItem.leftBarButtonItem?.action = #selector(self.back(sender:))
    self.navigationItem.leftBarButtonItem?.target = self
}

@objc func back(sender: UIBarButtonItem) {

}

0

다음은 사용자 정의 뒤로 버튼을 만들 필요없이 무료로 제공되는 UINavigationController 왼쪽 버튼 기능을 모두 포기할 수있는 가장 간단한 Swift 5 솔루션입니다.

Brandon A가 위에서 권장 한대로 구현해야합니다.UINavigationControllerDelegate 당신이 그것을 반환하기 전에와 상호 작용 할 뷰 컨트롤러. 좋은 방법은 수동 또는 자동으로 수행하고 사용자 정의 완료 단추 또는 뒤로 단추에서 동일한 코드를 재사용 할 수있는 풀기 segue를 만드는 것입니다.

먼저 뷰 컨트롤러를 관심있는 컨트롤러 (돌아가고 싶은 것으로 감지)를 내비게이션 컨트롤러의 델리게이트로 만듭니다 viewDidLoad.

override func viewDidLoad() {
    super.viewDidLoad()
    navigationController?.delegate = self
}

둘째, 파일 하단에 재정의하는 확장자를 추가하십시오. navigationController(willShow:animated:)

extension PickerTableViewController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController,
                              willShow viewController: UIViewController,
                              animated: Bool) {

        if let _ = viewController as? EditComicBookViewController {

            let selectedItemRow = itemList.firstIndex(of: selectedItemName)
            selectedItemIndex = IndexPath(row: selectedItemRow!, section: 0)

            if let selectedCell = tableView.cellForRow(at: selectedItemIndex) {
                performSegue(withIdentifier: "PickedItem", sender: selectedCell)
            }
        }
    }
}

귀하의 질문에가 포함되어 UITableViewController있으므로 사용자가 누른 행의 색인 경로를 얻는 방법을 포함 시켰습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.