บล็อกเสร็จสิ้นสำหรับ popViewController


115

เมื่อยกเลิกการใช้ตัวควบคุมมุมมองแบบโมดอลdismissViewControllerจะมีตัวเลือกในการจัดเตรียมบล็อกที่สมบูรณ์ มีค่าเทียบเท่าสำหรับpopViewController?

อาร์กิวเมนต์เสร็จสมบูรณ์ค่อนข้างมีประโยชน์ ตัวอย่างเช่นฉันสามารถใช้เพื่อระงับการลบแถวออกจากมุมมองตารางจนกว่าโมดอลจะปิดหน้าจอเพื่อให้ผู้ใช้เห็นภาพเคลื่อนไหวของแถว เมื่อกลับจากตัวควบคุมมุมมองแบบพุชฉันต้องการโอกาสเดียวกันนี้

ฉันได้ลองวางpopViewControllerในUIViewบล็อกแอนิเมชั่นซึ่งฉันสามารถเข้าถึงบล็อกที่สมบูรณ์ได้ อย่างไรก็ตามสิ่งนี้ก่อให้เกิดผลข้างเคียงบางอย่างที่ไม่ต้องการกับมุมมองที่ถูกดึงไป

หากไม่มีวิธีการดังกล่าวมีวิธีแก้ปัญหาอย่างไร


stackoverflow.com/a/33767837/2774520ฉันคิดว่าวิธีนี้เป็นวิธีที่ดีที่สุด
Oleksii Nezhyborets


3
สำหรับปี 2018 สิ่งนี้ง่ายและเป็นมาตรฐานมาก: stackoverflow.com/a/43017103/294884
Fattie

คำตอบ:


201

ฉันรู้ว่าคำตอบได้รับการยอมรับเมื่อสองปีที่แล้วอย่างไรก็ตามคำตอบนี้ไม่สมบูรณ์

ไม่มีทางทำในสิ่งที่คุณต้องการได้ทันที

สิ่งนี้ถูกต้องในทางเทคนิคเนื่องจากUINavigationControllerAPI ไม่มีตัวเลือกใด ๆ สำหรับสิ่งนี้ อย่างไรก็ตามการใช้เฟรมเวิร์ก CoreAnimation คุณสามารถเพิ่มบล็อกที่สมบูรณ์ให้กับแอนิเมชั่นพื้นฐานได้:

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    // handle completion here
}];

[self.navigationController popViewControllerAnimated:YES];

[CATransaction commit];

บล็อกเสร็จสิ้นจะถูกเรียกทันทีที่ภาพเคลื่อนไหวที่ใช้โดยpopViewControllerAnimated:สิ้นสุด ฟังก์ชันนี้พร้อมใช้งานตั้งแต่ iOS 4


5
ฉันใส่ไว้ในส่วนขยายของ UINavigationController ใน Swift:extension UINavigationController { func popViewControllerWithHandler(handler: ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(handler) self.popViewControllerAnimated(true) CATransaction.commit() } }
Arbitur

1
ดูเหมือนจะไม่ได้ผลสำหรับฉันเมื่อฉันทำ FinishHandler ในการปิดการใช้งาน ViewController มุมมองที่นำเสนอเป็นส่วนหนึ่งของลำดับชั้นมุมมอง เมื่อฉันทำเช่นเดียวกันกับ CATransaction ฉันได้รับคำเตือนว่ามุมมองไม่ได้เป็นส่วนหนึ่งของลำดับชั้นของมุมมอง
moger777

1
ตกลงดูเหมือนว่างานของคุณถ้าคุณย้อนกลับบล็อกเริ่มต้นและเสร็จสิ้น ขออภัยเกี่ยวกับการโหวตลง แต่ stack overflow ไม่ยอมให้ฉันเปลี่ยน :(
moger777

7
ใช่ดูเหมือนว่ามันจะยอดเยี่ยม แต่ดูเหมือนจะไม่ได้ผล (อย่างน้อยใน iOS 8) บล็อกที่เสร็จสมบูรณ์จะถูกเรียกทันที น่าจะเป็นเพราะส่วนผสมของภาพเคลื่อนไหวหลักกับภาพเคลื่อนไหวสไตล์ UIView
ติด

5
สิ่งนี้ไม่ได้ผล
durazno

52

สำหรับเวอร์ชันiOS9 SWIFT - ทำงานได้อย่างมีเสน่ห์ (ยังไม่ได้ทดสอบสำหรับเวอร์ชันก่อนหน้านี้) ขึ้นอยู่กับคำตอบนี้

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

        if let coordinator = transitionCoordinator() where animated {
            coordinator.animateAlongsideTransition(nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }

    func popViewController(animated: Bool, completion: () -> ()) {
        popViewControllerAnimated(animated)

        if let coordinator = transitionCoordinator() where animated {
            coordinator.animateAlongsideTransition(nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}

จะไม่ทำงานหากไม่เคลื่อนไหวควรทำในรันลูปถัดไปให้เสร็จสมบูรณ์
rshev

@rshev ทำไมใน runloop ต่อไป?
Ben Sinclair

@ แอนดี้จากสิ่งที่ฉันจำได้ว่าได้ทดลองกับสิ่งนี้ยังไม่มีการเผยแพร่อะไรบางอย่างในตอนนั้น ลองทดลองกับมันชอบฟังว่ามันเหมาะกับคุณอย่างไร
rshev

@rshev ฉันคิดว่าฉันเคยเป็นแบบเดียวกันมาก่อนฉันต้องตรวจสอบอีกครั้ง การทดสอบปัจจุบันทำงานได้ดี
Ben Sinclair

1
@LanceSamaria ฉันขอแนะนำให้ใช้ viewDidDisappear ตรวจสอบว่า navbar พร้อมใช้งานหรือไม่หากไม่มี - ไม่แสดงใน navbar ดังนั้นจึงมีการโผล่ขึ้นมา if (self.navigationController == nil) {trigger your action}
HotJard

33

ฉันสร้างSwiftเวอร์ชันที่มีส่วนขยายด้วยคำตอบ@JorisKluivers

นี้จะเรียกปิดเสร็จสิ้นหลังจากที่เคลื่อนไหวจะทำทั้งสองและpushpop

extension UINavigationController {
    func popViewControllerWithHandler(completion: ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.popViewControllerAnimated(true)
        CATransaction.commit()
    }
    func pushViewController(viewController: UIViewController, completion: ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: true)
        CATransaction.commit()
    }
}

สำหรับฉันใน iOS 8.4 ที่เขียนใน ObjC บล็อกจะยิงครึ่งหนึ่งของภาพเคลื่อนไหว สิ่งนี้จะเกิดขึ้นในช่วงเวลาที่เหมาะสมหรือไม่ถ้าเขียนด้วย Swift (8.4)
Julian F.Winert

@Arbitur complete block ถูกเรียกหลังจากโทรpopViewControllerหรือpushViewControllerแต่ถ้าคุณตรวจสอบว่า topViewController คืออะไรในภายหลังคุณจะสังเกตเห็นว่ามันยังคงเป็นแบบเก่าเหมือนpopหรือpushไม่เคยเกิดขึ้น ...
Bogdan Razvan

@BogdanRazvan หลังจากนั้นคืออะไร? การปิดสำเร็จของคุณถูกเรียกเมื่อภาพเคลื่อนไหวเสร็จสมบูรณ์หรือไม่?
Arbitur

17

SWIFT 4.1

extension UINavigationController {
func pushToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.pushViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popViewController(animated: animated)
    CATransaction.commit()
}

func popToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popToRootViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToRootViewController(animated: animated)
    CATransaction.commit()
}
}

17

ฉันมีปัญหาเดียวกัน และเนื่องจากฉันต้องใช้มันหลายครั้งและภายในกลุ่มของบล็อกที่สมบูรณ์ฉันจึงสร้างโซลูชันทั่วไปนี้ในคลาสย่อย UINavigationController:

- (void) navigationController:(UINavigationController *) navigationController didShowViewController:(UIViewController *) viewController animated:(BOOL) animated {
    if (_completion) {
        dispatch_async(dispatch_get_main_queue(),
        ^{
            _completion();
            _completion = nil;
         });
    }
}

- (UIViewController *) popViewControllerAnimated:(BOOL) animated completion:(void (^)()) completion {
    _completion = completion;
    return [super popViewControllerAnimated:animated];
}

ทะลึ่ง

@interface NavigationController : UINavigationController <UINavigationControllerDelegate>

และ

@implementation NavigationController {
    void (^_completion)();
}

และ

- (id) initWithRootViewController:(UIViewController *) rootViewController {
    self = [super initWithRootViewController:rootViewController];
    if (self) {
        self.delegate = self;
    }
    return self;
}

1
ฉันชอบโซลูชันนี้มากฉันจะลองใช้กับหมวดหมู่และวัตถุที่เกี่ยวข้อง
spstanley

@spstanley คุณต้องเผยแพร่พ็อดนี้ :)
k06a

เวอร์ชั่น Swift -> stackoverflow.com/a/60090678/4010725
WILL K.

15

ไม่มีทางทำในสิ่งที่คุณต้องการได้ทันที กล่าวคือไม่มีเมธอดใดที่มีบล็อกที่สมบูรณ์สำหรับการเปิดตัวควบคุมมุมมองจากสแต็ก nav

viewDidAppearสิ่งที่ผมจะทำคือการใส่ตรรกะใน ซึ่งจะถูกเรียกเมื่อมุมมองเสร็จสิ้นมาบนหน้าจอ จะถูกเรียกสำหรับสถานการณ์ที่แตกต่างกันทั้งหมดของตัวควบคุมมุมมองที่ปรากฏขึ้น แต่ก็น่าจะใช้ได้

หรือคุณอาจใช้UINavigationControllerDelegateวิธีnavigationController:didShowViewController:animated:ทำสิ่งที่คล้ายกัน สิ่งนี้เรียกว่าเมื่อตัวควบคุมการนำทางเสร็จสิ้นการผลักหรือเปิดตัวควบคุมมุมมอง


ฉันพยายามแล้ว ฉันกำลังจัดเก็บอาร์เรย์ของ 'ดัชนีแถวที่ถูกลบ' และเมื่อใดก็ตามที่มุมมองปรากฏขึ้นให้ตรวจสอบดูว่าจำเป็นต้องลบสิ่งใด มันดูเทอะทะอย่างรวดเร็ว แต่ฉันอาจจะลองอีกครั้ง ฉันสงสัยว่าทำไม Apple ถึงให้การเปลี่ยนแปลงครั้งเดียว แต่ไม่ใช่อีกครั้ง?
Ben Packard

1
มันใหม่มากในไฟล์dismissViewController. popViewControllerบางทีมันอาจจะมา ยื่นเรดาร์ :-)
mattjgalloway

แม้ว่าอย่างจริงจังให้ยื่นเรดาร์ มีแนวโน้มที่จะเข้ามาหากมีคนขอ
mattjgalloway

1
นั่นคือสถานที่ที่เหมาะสมที่จะขอมัน มีตัวเลือกสำหรับการจัดหมวดหมู่เป็น 'คุณลักษณะ'
mattjgalloway

3
คำตอบนี้ไม่ถูกต้องทั้งหมด แม้ว่าคุณจะไม่สามารถตั้งค่าบล็อกรูปแบบใหม่ได้เหมือนเปิด-dismissViewController:animated:completionBlock:แต่คุณสามารถรับภาพเคลื่อนไหวผ่านตัวแทนของตัวควบคุมการนำทางได้ หลังจากภาพเคลื่อนไหวเสร็จสิ้น-navigationController:didShowViewController:animated:จะมีการเรียกผู้ร่วมประชุมและคุณสามารถทำทุกอย่างที่ต้องการได้ที่นั่น
Jason Coco

13

ทำงานโดยมีหรือไม่มีภาพเคลื่อนไหวอย่างถูกต้องและยังรวมถึงpopToRootViewController:

 // updated for Swift 3.0
extension UINavigationController {

  private func doAfterAnimatingTransition(animated: Bool, completion: @escaping (() -> Void)) {
    if let coordinator = transitionCoordinator, animated {
      coordinator.animate(alongsideTransition: nil, completion: { _ in
        completion()
      })
    } else {
      DispatchQueue.main.async {
        completion()
      }
    }
  }

  func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping (() ->     Void)) {
    pushViewController(viewController, animated: animated)
    doAfterAnimatingTransition(animated: animated, completion: completion)
  }

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

  func popToRootViewController(animated: Bool, completion: @escaping (() -> Void)) {
    popToRootViewController(animated: animated)
    doAfterAnimatingTransition(animated: animated, completion: completion)
  }
}

มีเหตุผลใดเป็นพิเศษทำไมคุณถึงเรียกcompletion()async?
เลวีอาธาน

1
เมื่อการเคลื่อนไหวด้วยผู้ประสานงานcompletionจะไม่ถูกดำเนินการบนรันลูปเดียวกัน สิ่งนี้รับประกันว่าcompletionจะไม่ทำงานบนรันลูปเดียวกันเมื่อไม่เคลื่อนไหว จะดีกว่าถ้าไม่มีความไม่ลงรอยกันแบบนี้
rshev

11

จากคำตอบของ @ HotJard เมื่อสิ่งที่คุณต้องการมีเพียงไม่กี่บรรทัดของโค้ด ง่ายและรวดเร็ว

Swift 4 :

_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
    doWhatIWantAfterContollerHasPopped()
}

6

สำหรับปี 2561 ...

ถ้าคุณมีสิ่งนี้ ...

    navigationController?.popViewController(animated: false)
    // I want this to happen next, help! ->
    nextStep()

และคุณต้องการเพิ่มความสมบูรณ์ ...

    CATransaction.begin()
    navigationController?.popViewController(animated: true)
    CATransaction.setCompletionBlock({ [weak self] in
       self?.nextStep() })
    CATransaction.commit()

มันง่ายมาก

เคล็ดลับที่มีประโยชน์ ...

เป็นข้อตกลงเดียวกันสำหรับสะดวก popToViewControllerโทรที่

โดยทั่วไปคือคุณมีหน้าจอเป็นล้าน ๆ หน้าจอ เมื่อเสร็จแล้วคุณกลับไปที่หน้าจอ "ฐาน" จนสุดแล้วเปิดแอปขึ้นมาในที่สุด

ในหน้าจอ "ฐาน" เพื่อไปที่ "ย้อนกลับ" popToViewController(self

func onboardingStackFinallyComplete() {
    
    CATransaction.begin()
    navigationController?.popToViewController(self, animated: false)
    CATransaction.setCompletionBlock({ [weak self] in
        guard let self = self else { return }
        .. actually launch the main part of the app
    })
    CATransaction.commit()
}

5

บล็อกที่เสร็จสมบูรณ์ถูกเรียกหลังจากเมธอด viewDidDisappear ถูกเรียกบนตัวควบคุมมุมมองที่นำเสนอดังนั้นการใส่โค้ดในเมธอด viewDidDisappear ของตัวควบคุมมุมมองที่โผล่ขึ้นมาควรทำงานเหมือนกับบล็อกที่เสร็จสมบูรณ์


แน่นอน - ยกเว้นคุณต้องจัดการทุกกรณีที่มุมมองหายไปด้วยเหตุผลอื่น
Ben Packard

1
@ BenPackard ใช่และเช่นเดียวกันกับการวางไว้ใน viewDidAppear ในคำตอบที่คุณยอมรับ
rdelmar

5

คำตอบ Swift 3 ขอบคุณสำหรับคำตอบนี้: https://stackoverflow.com/a/28232570/3412567

    //MARK:UINavigationController Extension
extension UINavigationController {
    //Same function as "popViewController", but allow us to know when this function ends
    func popViewControllerWithHandler(completion: @escaping ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.popViewController(animated: true)
        CATransaction.commit()
    }
    func pushViewController(viewController: UIViewController, completion: @escaping ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: true)
        CATransaction.commit()
    }
}

4

เวอร์ชัน Swift 4 พร้อมพารามิเตอร์ viewController ที่เป็นทางเลือกเพื่อป๊อปอัปไปยังรายการเฉพาะ

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

        pushViewController(viewController, animated: animated)

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

func popViewController(viewController: UIViewController? = nil, 
    animated: Bool, completion: @escaping () -> ()) {
        if let viewController = viewController {
            popToViewController(viewController, animated: animated)
        } else {
            popViewController(animated: animated)
        }

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

คำตอบที่ยอมรับดูเหมือนจะใช้ได้ในสภาพแวดล้อมการพัฒนาของฉันกับอีมูเลเตอร์ / อุปกรณ์ทั้งหมดที่ฉันมี แต่ฉันยังคงได้รับรายงานข้อบกพร่องจากผู้ใช้ที่ใช้งานจริง ไม่แน่ใจว่าวิธีนี้จะช่วยแก้ปัญหาการผลิตได้หรือไม่ แต่ขอให้เพิ่มคะแนนเพื่อให้มีคนลองใช้หากได้รับปัญหาเดียวกันจากคำตอบที่ยอมรับ
ฌอน

4

ทำความสะอาดเวอร์ชันSwift 4ตามคำตอบนี้

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

    func popViewController(animated: Bool, completion: @escaping () -> Void) -> UIViewController? {
        let viewController = self.popViewController(animated: animated)
        self.callCompletion(animated: animated, completion: completion)
        return viewController
    }

    private func callCompletion(animated: Bool, completion: @escaping () -> Void) {
        if animated, let coordinator = self.transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}


2

2020 Swift 5.1 วิธี

โซลูชันนี้รับประกันว่าจะดำเนินการเสร็จสิ้นหลังจากที่ popViewController ทำงานเสร็จสมบูรณ์ คุณสามารถทดสอบได้โดยดำเนินการอื่นบน NavigationController จนเสร็จสิ้น: ในโซลูชันอื่น ๆ ทั้งหมดที่อยู่เหนือ UINavigationController ยังคงยุ่งอยู่กับการทำงานของ popViewController และไม่ตอบสนอง

public class NavigationController: UINavigationController, UINavigationControllerDelegate
{
    private var completion: (() -> Void)?

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
        delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public override func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool)
    {
        if self.completion != nil {
            DispatchQueue.main.async(execute: {
                self.completion?()
                self.completion = nil
            })
        }
    }

    func popViewController(animated: Bool, completion: @escaping () -> Void) -> UIViewController?
    {
        self.completion = completion
        return super.popViewController(animated: animated)
    }
}

1

เพื่อความสมบูรณ์ฉันได้จัดทำหมวด Objective-C พร้อมใช้งาน:

// UINavigationController+CompletionBlock.h

#import <UIKit/UIKit.h>

@interface UINavigationController (CompletionBlock)

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

@end
// UINavigationController+CompletionBlock.m

#import "UINavigationController+CompletionBlock.h"

@implementation UINavigationController (CompletionBlock)

- (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion {
    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        completion();
    }];

    UIViewController *vc = [self popViewControllerAnimated:animated];

    [CATransaction commit];

    return vc;
}

@end

1

ฉันทำได้อย่างแม่นยำโดยใช้บล็อก ฉันต้องการให้ตัวควบคุมผลลัพธ์ที่ดึงมาแสดงแถวที่เพิ่มโดยมุมมองโมดอลเพียงครั้งเดียวที่ออกจากหน้าจอจนสุดผู้ใช้จึงเห็นการเปลี่ยนแปลงที่เกิดขึ้น ในการเตรียมพร้อมสำหรับการทำต่อซึ่งมีหน้าที่แสดงตัวควบคุมมุมมองโมดอลฉันตั้งค่าบล็อกที่ต้องการดำเนินการเมื่อโมดอลหายไป และในตัวควบคุมมุมมองโมดอลฉันจะแทนที่ viewDidDissapear แล้วเรียกบล็อก ฉันเพียงแค่เริ่มอัปเดตเมื่อโมดอลกำลังจะปรากฏขึ้นและสิ้นสุดการอัปเดตเมื่อมันหายไป แต่นั่นเป็นเพราะฉันใช้ NSFetchedResultsController อย่างไรก็ตามคุณสามารถทำอะไรก็ได้ที่คุณต้องการภายในบล็อก

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"addPassword"]){

        UINavigationController* nav = (UINavigationController*)segue.destinationViewController;
        AddPasswordViewController* v = (AddPasswordViewController*)nav.topViewController;

...

        // makes row appear after modal is away.
        [self.tableView beginUpdates];
        [v setViewDidDissapear:^(BOOL animated) {
            [self.tableView endUpdates];
        }];
    }
}

@interface AddPasswordViewController : UITableViewController<UITextFieldDelegate>

...

@property (nonatomic, copy, nullable) void (^viewDidDissapear)(BOOL animated);

@end

@implementation AddPasswordViewController{

...

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    if(self.viewDidDissapear){
        self.viewDidDissapear(animated);
    }
}

@end

1

ใช้ส่วนขยายถัดไปในโค้ดของคุณ: (Swift 4)

import UIKit

extension UINavigationController {

    func popViewController(animated: Bool = true, completion: @escaping () -> Void) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        popViewController(animated: animated)
        CATransaction.commit()
    }

    func pushViewController(_ viewController: UIViewController, animated: Bool = true, completion: @escaping () -> Void) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        pushViewController(viewController, animated: animated)
        CATransaction.commit()
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.