ইউআইএনএভিগেশন কন্ট্রোলার "পুশভিউ কনট্রোলার: অ্যানিমেটেড" এর জন্য সম্পূর্ণ হ্যান্ডলার?


110

আমি UINavigationControllerপরবর্তী ভিউ নিয়ন্ত্রণকারীদের উপস্থাপন করতে একটি ব্যবহার করে একটি অ্যাপ তৈরি করতে চলেছি । আইওএস 5 এর সাথে এখানে উপস্থাপনের জন্য নতুন পদ্ধতি UIViewControllers:

presentViewController:animated:completion:

এখন আমি আমাকে জিজ্ঞাসা করছি কেন সেখানে একটি সমাপ্তি হ্যান্ডলার নেই UINavigationController? ঠিক আছে

pushViewController:animated:

নতুনের মতো আমার নিজের সম্পূর্ণতা হ্যান্ডলারটি তৈরি করা কি সম্ভব presentViewController:animated:completion:?


2
সমাপ্তি হ্যান্ডলার হিসাবে ঠিক একই জিনিস নয় viewDidAppear:animated:তবে আপনার ভিউ কন্ট্রোলার পর্দায় প্রদর্শিত প্রত্যেকবারই কোডটি কার্যকর করতে দিন ( viewDidLoadকেবলমাত্র প্রথমবার আপনার ভিউ কন্ট্রোলারটি লোড করা হয়)
ম্যাক্সি

@ মক্সি, আপনার অর্থ কি-(void)viewDidAppear:(BOOL)animated
জর্জ

2
2018 এর জন্য ... সত্যিই এটি: স্ট্যাকওভারফ্লো.com
a

উত্তর:


139

অন্যটির জন্য সমান উত্তর দেখুন এবং আরও আপ টু ডেট সমাধান

UINavigationControllerঅ্যানিমেশনগুলি চালিত হয় CoreAnimation, সুতরাং কোডটির মধ্যে কোডটি অন্তর্ভুক্ত করা CATransactionএবং এইভাবে একটি সমাপ্তি ব্লক সেট করা বুদ্ধিমান হয়ে উঠবে ।

দ্রুত :

দ্রুতগতির জন্য আমি এ জাতীয় একটি এক্সটেনশন তৈরি করার পরামর্শ দিচ্ছি

extension UINavigationController {

  public func pushViewController(viewController: UIViewController,
                                 animated: Bool,
                                 completion: @escaping (() -> Void)?) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    pushViewController(viewController, animated: animated)
    CATransaction.commit()
  }

}

ব্যবহার:

navigationController?.pushViewController(vc, animated: true) {
  // Animation done
}

উদ্দেশ্য গ

শিরোলেখ:

#import <UIKit/UIKit.h>

@interface UINavigationController (CompletionHandler)

- (void)completionhandler_pushViewController:(UIViewController *)viewController
                                    animated:(BOOL)animated
                                  completion:(void (^)(void))completion;

@end

বাস্তবায়ন:

#import "UINavigationController+CompletionHandler.h"
#import <QuartzCore/QuartzCore.h>

@implementation UINavigationController (CompletionHandler)

- (void)completionhandler_pushViewController:(UIViewController *)viewController 
                                    animated:(BOOL)animated 
                                  completion:(void (^)(void))completion 
{
    [CATransaction begin];
    [CATransaction setCompletionBlock:completion];
    [self pushViewController:viewController animated:animated];
    [CATransaction commit];
}

@end

1
আমি বিশ্বাস করি (পরীক্ষিত হয়নি) যদি উপস্থাপিত ভিউ কন্ট্রোলার এর ভিডডিলড বা ভিউউইল অ্যাপয়ার বাস্তবায়নগুলির মধ্যে অ্যানিমেশনগুলিকে ট্রিগার করে তবে এটি সঠিক ফলাফল প্রদান করতে পারে। আমি মনে করি পুশভিউ কনট্রোলার: অ্যানিমেটেড: ফেরত দেওয়ার আগে এই অ্যানিমেশনগুলি শুরু করা হবে thus সুতরাং, নতুন-ট্রিগার হওয়া অ্যানিমেশনগুলি শেষ না হওয়া পর্যন্ত সমাপ্তি হ্যান্ডলারটি কল হবে না।
ম্যাট এইচ।

1
@MattH। কি একটি দম্পতি আজ সন্ধ্যায় পরীক্ষা এবং এটি ব্যবহার করে যখন দেখে মনে হচ্ছে pushViewController:animated:বা popViewController:animated, viewDidLoadএবং viewDidAppearকল পরবর্তী runloop চক্র ঘটতে। সুতরাং আমার ধারণাটি হ'ল এমনকি যদি এই পদ্ধতিগুলি অ্যানিমেশনগুলি আহ্বান করে তবে তারা কোড উদাহরণে প্রদত্ত লেনদেনের অংশ হবে না। এটা কি আপনার উদ্বেগ ছিল? কারণ এই সমাধানটি খুব সহজ।
লেফেলমানিয়া

1
এই প্রশ্নটির দিকে ফিরে তাকানো, আমি সাধারণভাবে @ ম্যাথ দ্বারা উল্লিখিত উদ্বেগগুলি মনে করি think এবং @ লিফেলমানিয়া এই সমাধানের সাথে একটি বৈধ সমস্যা হাইলাইট করে - এটি শেষ পর্যন্ত ধরে নেয় ধাক্কাটি শেষ হওয়ার পরে লেনদেন শেষ হবে, তবে কাঠামোটি এই আচরণের গ্যারান্টি দেয় না। didShowViewControllerযদিও এটির মধ্যে প্রশ্নাবলীর ভিউ কন্ট্রোলার প্রদর্শিত হচ্ছে তার চেয়ে গ্যারান্টিযুক্ত । যদিও এই সমাধানটি দুর্দান্তভাবে সহজ, আমি তার "ভবিষ্যত-প্রমাণ-নেস" নিয়ে প্রশ্ন করব। আইওএস 7/8
স্যাম

8
এটি iOS 9 ডিভাইসে নির্ভরযোগ্যভাবে কাজ করবে বলে মনে হচ্ছে না। বিকল্পের জন্য নীচে আমার বা @ পার্সের উত্তরগুলি দেখুন
মাইক স্প্রেগ

1
নিঃসন্দেহে জেডএইজেনবার্গ আমার উত্তরটি এই পৃথিবীতে ডায়নোসর কোড ~~ 2 বছর বয়সী
chrs

95

আইওএস 7+ সুইফ্ট

সুইফট 4:

// 2018.10.30 par:
//   I've updated this answer with an asynchronous dispatch to the main queue
//   when we're called without animation. This really should have been in the
//   previous solutions I gave but I forgot to add it.
extension UINavigationController {
    public func pushViewController(
        _ viewController: UIViewController,
        animated: Bool,
        completion: @escaping () -> Void)
    {
        pushViewController(viewController, animated: animated)

        guard animated, let coordinator = transitionCoordinator else {
            DispatchQueue.main.async { completion() }
            return
        }

        coordinator.animate(alongsideTransition: nil) { _ in completion() }
    }

    func popViewController(
        animated: Bool,
        completion: @escaping () -> Void)
    {
        popViewController(animated: animated)

        guard animated, let coordinator = transitionCoordinator else {
            DispatchQueue.main.async { completion() }
            return
        }

        coordinator.animate(alongsideTransition: nil) { _ in completion() }
    }
}

সম্পাদনা: আমি আমার মূল উত্তরের একটি সুইফট 3 সংস্করণ যুক্ত করেছি। এই সংস্করণে আমি সুইফট 2 সংস্করণে দেখানো উদাহরণ সহ-অ্যানিমেশনটি সরিয়ে দিয়েছি বলে মনে হচ্ছে এটি প্রচুর লোককে বিভ্রান্ত করেছে।

সুইফট 3:

import UIKit

// Swift 3 version, no co-animation (alongsideTransition parameter is nil)
extension UINavigationController {
    public func pushViewController(
        _ viewController: UIViewController,
        animated: Bool,
        completion: @escaping (Void) -> Void)
    {
        pushViewController(viewController, animated: animated)

        guard animated, let coordinator = transitionCoordinator else {
            completion()
            return
        }

        coordinator.animate(alongsideTransition: nil) { _ in completion() }
    }
}

সুইফট 2:

import UIKit

// Swift 2 Version, shows example co-animation (status bar update)
extension UINavigationController {
    public func pushViewController(
        viewController: UIViewController,
        animated: Bool,
        completion: Void -> Void)
    {
        pushViewController(viewController, animated: animated)

        guard animated, let coordinator = transitionCoordinator() else {
            completion()
            return
        }

        coordinator.animateAlongsideTransition(
            // pass nil here or do something animated if you'd like, e.g.:
            { context in
                viewController.setNeedsStatusBarAppearanceUpdate()
            },
            completion: { context in
                completion()
            }
        )
    }
}

1
আপনি কোন উপাচার্যকে তার স্ট্যাটাস বারটি আপডেট করতে বলছেন তার কোনও কারণ আছে? এটি nilঅ্যানিমেশন ব্লক হিসাবে উত্তমরূপে কাজ করে বলে মনে হচ্ছে ।
মাইক স্প্রেগ

2
এটি সমান্তরাল অ্যানিমেশন হিসাবে আপনি যা করতে পারেন তার উদাহরণ (এটির উপরে মন্তব্যটি এটি alচ্ছিক নির্দেশ করে)। পাসিংও nilপুরোপুরি কার্যকর করার মতো জিনিস।
পার

1
@ পার, আপনার কি আরও বেশি প্রতিরক্ষামূলক হওয়া উচিত এবং শূন্যতার সময় যখন সমাপ্তির কল করা উচিত transitionCoordinator?
অরেলিন পোর্টে

@ অরলিনপোর্ট এটি একটি দুর্দান্ত ক্যাচ এবং আমি হ্যাঁ বলি, আপনার উচিত। আমি উত্তর আপডেট করব।
পার

1
@ কাবাউনস আমি এটির বিষয়ে 100% নিশ্চিত নই কারণ আমি এটি ঘটতে দেখিনি, তবে আপনি যদি transitionCoordinatorএটি না দেখেন তবে সম্ভবত আপনি নেভিগেশন কন্ট্রোলারের জীবনচক্রের খুব শীঘ্রই এই ফাংশনটিকে কল করছেন। viewWillAppear()অ্যানিমেশন সহ কোনও ভিউ কন্ট্রোলারকে ধাক্কা দেওয়ার আগে ডাকা হলেও কমপক্ষে অপেক্ষা করুন ।
পার

28

পার এর উত্তরের ভিত্তিতে (যা কেবলমাত্র আইওএস 9 এর সাথে কাজ করেছিল), তবে সহজ এবং অন্য কোনও নিখোঁজ (যা সম্পূর্ণ হওয়ার আগে কখনও ডাকা হত না):

extension UINavigationController {
    func pushViewController(_ viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) {
        pushViewController(viewController, animated: animated)

        if animated, let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }

    func popViewController(animated: Bool, completion: @escaping () -> Void) {
        popViewController(animated: animated)

        if animated, let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}

আমার জন্য কাজ করে না। ট্রানজিশনকর্ডিনেটর আমার জন্য শূন্য।
tcurdt

আমার জন্য কাজ কর. এছাড়াও এটিকে আরও ভালভাবে গ্রহণ করা হয় কারণ অ্যানিমেশন সমাপ্তি সবসময় ধাক্কা সমাপ্তির সমান হয় না।
আন্তোন প্লেবানোভিচ

অ অ্যানিমেটেড কেসের জন্য আপনি একটি DispatchQueue.main.async মিস করছেন। এই পদ্ধতির চুক্তিটি হ'ল সমাপ্তি হ্যান্ডলারটিকে অবিচ্ছিন্নভাবে বলা হয়, আপনার এটি লঙ্ঘন করা উচিত নয় কারণ এটি সূক্ষ্ম ত্রুটিগুলি বাড়ে।
ওয়ার্নার আলটিউইচার

24

বর্তমানে UINavigationControllerএই সমর্থন করে না। কিন্তু UINavigationControllerDelegateআপনি ব্যবহার করতে পারেন যে আছে ।

এটি সম্পাদন করার একটি সহজ উপায় হ'ল সাবক্লাসিং UINavigationControllerএবং একটি সমাপ্তি ব্লক সম্পত্তি যুক্ত করে:

@interface PbNavigationController : UINavigationController <UINavigationControllerDelegate>

@property (nonatomic,copy) dispatch_block_t completionBlock;

@end


@implementation PbNavigationController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.delegate = self;
    }
    return self;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    NSLog(@"didShowViewController:%@", viewController);

    if (self.completionBlock) {
        self.completionBlock();
        self.completionBlock = nil;
    }
}

@end

নতুন দৃশ্যের নিয়ামককে চাপ দেওয়ার আগে আপনাকে সমাপ্তি ব্লক সেট করতে হবে:

UIViewController *vc = ...;
((PbNavigationController *)self.navigationController).completionBlock = ^ {
    NSLog(@"COMPLETED");
};
[self.navigationController pushViewController:vc animated:YES];

এই নতুন সাবক্লাসটি হয় হয় ইন্টারফেস বিল্ডারে নির্ধারিত হতে পারে বা এইভাবে প্রোগ্রামের মতো ব্যবহার করা যেতে পারে:

PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController];

8
নিয়ামকগুলি দেখার জন্য ম্যাপযুক্ত সমাপ্তি ব্লকগুলির একটি তালিকা যুক্ত করা সম্ভবত এটি সবচেয়ে কার্যকর হবে এবং সম্ভবত একটি নতুন পদ্ধতি, এটি সম্ভবত pushViewController:animated:completion:একটি সুন্দর সমাধান হিসাবে বিবেচনা করবে।
হাইপারবোলে

1
এনবি 2018 এর জন্য এটি সত্যিই কেবল এটিই ... stackoverflow.com/a/43017103/294884
ফ্যাটি

8

এখানে পপ সহ সুইফট 4 সংস্করণ।

extension UINavigationController {
    public func pushViewController(viewController: UIViewController,
                                   animated: Bool,
                                   completion: (() -> Void)?) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        pushViewController(viewController, animated: animated)
        CATransaction.commit()
    }

    public func popViewController(animated: Bool,
                                  completion: (() -> Void)?) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        popViewController(animated: animated)
        CATransaction.commit()
    }
}

সেক্ষেত্রে অন্য কারও প্রয়োজন হয়।


আপনি যদি এটিতে একটি সাধারণ পরীক্ষা চালান, আপনি দেখতে পাবেন যে অ্যানিমেশনটি শেষ হওয়ার আগেই সমাপ্তি ব্লকটি জ্বলে উঠবে। সুতরাং এটি সম্ভবত যা খুঁজছেন তা সরবরাহ করে না।
অশ্বারোহী 7

7

@ ক্লাসের উত্তরে (এবং এই প্রশ্নের ফলস্বরূপ ) প্রসারিত করতে আমি সরাসরি ধাকানোর পদ্ধতিতে সম্পূর্ণতা ব্লক যুক্ত করেছি:

@interface PbNavigationController : UINavigationController <UINavigationControllerDelegate>

@property (nonatomic,copy) dispatch_block_t completionBlock;
@property (nonatomic,strong) UIViewController * pushedVC;

@end


@implementation PbNavigationController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        self.delegate = self;
    }
    return self;
}

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    NSLog(@"didShowViewController:%@", viewController);

    if (self.completionBlock && self.pushedVC == viewController) {
        self.completionBlock();
    }
    self.completionBlock = nil;
    self.pushedVC = nil;
}

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    if (self.pushedVC != viewController) {
        self.pushedVC = nil;
        self.completionBlock = nil;
    }
}

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(dispatch_block_t)completion {
    self.pushedVC = viewController;
    self.completionBlock = completion;
    [self pushViewController:viewController animated:animated];
}

@end

নিম্নলিখিত হিসাবে ব্যবহার করা:

UIViewController *vc = ...;
[(PbNavigationController *)self.navigationController pushViewController:vc animated:YES completion:^ {
    NSLog(@"COMPLETED");
}];

উজ্জ্বল। অনেক অনেক ধন্যবাদ
পেটর

if... (self.pushedVC == viewController) {ভুল. আপনার অবজেক্টের মধ্যে সমতা পরীক্ষা করতে হবে isEqual:, অর্থাত্[self.pushedVC isEqual:viewController]
ইভান আর

@ ইভানআর যা সম্ভবত আরও প্রযুক্তিগতভাবে সঠিক। উদাহরণস্বরূপকে অন্যভাবে তুলনা করতে আপনি কোনও ত্রুটি দেখেছেন?
স্যাম

@ স্যামটি বিশেষত এই উদাহরণটির সাথে নয় (এটি বাস্তবায়ন করেনি) তবে অন্যান্য বস্তুর সাথে সমতা পরীক্ষা করার ক্ষেত্রে Apple এটির জন্য অ্যাপলের ডক্স দেখুন: বিকাশকারী / অ্যাপ্লিকেশন / লাইব্রেরি / আইস / ডকুমেন্টেশন / জেনারেল / । আপনার তুলনা করার পদ্ধতিটি কি সর্বদা এই ক্ষেত্রে কার্যকর হয়?
ইভান আর

আমি এটি কাজ করে দেখিনি বা আমি আমার উত্তর পরিবর্তন করতে পারতাম। আমি যতদূর জানি আইওএস ক্রিয়াকলাপের সাথে অ্যান্ড্রয়েডের মতো ভিউ কন্ট্রোলারগুলিকে পুনরায় তৈরি করতে চতুর কিছু করে না। তবে হ্যাঁ, isEqualসম্ভবত তারা আরও কিছু প্রযুক্তিগতভাবে সঠিক হবে।
স্যাম

5

আইওএস .0.০ থেকে আপনি UIViewControllerTransitionCoordinatorএকটি ধাক্কা সমাপ্তি ব্লক যুক্ত করতে ব্যবহার করতে পারেন :

UINavigationController *nav = self.navigationController;
[nav pushViewController:vc animated:YES];

id<UIViewControllerTransitionCoordinator> coordinator = vc.transitionCoordinator;
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {

} completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
    NSLog(@"push completed");
}];

1
এটি ইউআইএনএভিগেশন কন্ট্রোলার পুশ, পপ ইত্যাদির মতো একই জিনিস নয়
জন উইলিস

3

সুইফট ২.০

extension UINavigationController : UINavigationControllerDelegate {
    private struct AssociatedKeys {
        static var currentCompletioObjectHandle = "currentCompletioObjectHandle"
    }
    typealias Completion = @convention(block) (UIViewController)->()
    var completionBlock:Completion?{
        get{
            let chBlock = unsafeBitCast(objc_getAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle), Completion.self)
            return chBlock as Completion
        }set{
            if let newValue = newValue {
                let newValueObj : AnyObject = unsafeBitCast(newValue, AnyObject.self)
                objc_setAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle, newValueObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }
    func popToViewController(animated: Bool,comp:Completion){
        if (self.delegate == nil){
            self.delegate = self
        }
        completionBlock = comp
        self.popViewControllerAnimated(true)
    }
    func pushViewController(viewController: UIViewController, comp:Completion) {
        if (self.delegate == nil){
            self.delegate = self
        }
        completionBlock = comp
        self.pushViewController(viewController, animated: true)
    }

    public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool){
        if let comp = completionBlock{
            comp(viewController)
            completionBlock = nil
            self.delegate = nil
        }
    }
}

2

এই আচরণটি যুক্ত করতে এবং বাহ্যিক প্রতিনিধি সেট করার ক্ষমতা ধরে রাখতে আরও কিছুটা পাইপওয়ার্ক লাগে।

এখানে একটি দলিলযুক্ত বাস্তবায়ন যা প্রতিনিধি কার্যকারিতা বজায় রাখে:

LBXCompletingNavigationController

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.