আইওএস-এ, কোনও মডেলকে খারিজ করতে কীভাবে নীচে টানবেন?


91

কোনও মডেলকে বরখাস্ত করার একটি সাধারণ উপায় হ'ল সোয়াইপ করা - আমরা কীভাবে ব্যবহারকারীকে মডেলটিকে নীচে টেনে আনতে দেব, যদি এটি যথেষ্ট পরিমাণে হয়, মডেলের বরখাস্ত হয়, অন্যথায় এটি মূল অবস্থানে ফিরে যায়?

উদাহরণস্বরূপ, আমরা এটি টুইটার অ্যাপের ফটো ভিউ বা স্ন্যাপচ্যাট এর "আবিষ্কার" মোডে ব্যবহার করতে পারি।

অনুরূপ থ্রেডগুলি নির্দেশ করে যে আমরা যখন কোনও ব্যবহারকারী সোয়াইপ করে তখন একটি মডেল ভিসি বরখাস্ত করার জন্য আমরা একটি ইউআইএসওয়াইপজেক্টরআরগনাইজার এবং [স্ব-খারিজ ভিউকন্ট্রোলার অ্যানিমেটেড ...] ব্যবহার করতে পারি। তবে এটি কেবলমাত্র একক সোয়াইপগুলি পরিচালনা করে, ব্যবহারকারীকে মোডালটিকে প্রায় টেনে আনতে দেয় না।


কাস্টম ইন্টারেক্টিভ ট্রানজিশনগুলি একবার দেখুন। আপনি এটি প্রয়োগ করতে পারেন। বিকাশকারী.এপ্লে.লাইবারি
প্রিরিলেস

রবার্ট চেনের github.com/ThornTechPublic/InteractiveModal রেপোতে উল্লেখ করেছেন এবং সবকিছু পরিচালনা করার জন্য একটি মোড়ক / হ্যান্ডলার শ্রেণি লিখেছিলেন। Github.com/chamira/ProjSetup/blob/master/appProject/_BasicSetup/…
ফার্নান্দো

@ চামিরা ফার্নান্দো, আপনার কোডটি দেখুন এবং এটি অনেক সাহায্য করে। এটির কোনও উপায় কি যাতে একের পরিবর্তে একাধিক দিক অন্তর্ভুক্ত করা যায়?
জেভন কাউয়েল

আমি করবো. সময়টি আজকাল বিশাল বাধা :(
চামিরা ফার্নান্দো

উত্তর:


95

আমি কেবলমাত্র কোনও মডেলটিকে খারিজ করার জন্য ইন্টারেক্টিভভাবে টেনে আনার জন্য একটি টিউটোরিয়াল তৈরি করেছি।

http://www.thorntech.com/2016/02/ios-tutorial-close-modal-dragging/

আমি প্রথমে এই বিষয়টিকে বিভ্রান্তিকর বলে মনে করেছি, সুতরাং টিউটোরিয়ালটি এটি ধাপে ধাপে তৈরি করে।

এখানে চিত্র বর্ণনা লিখুন

আপনি যদি কেবল কোডটি নিজে চালাতে চান তবে এটি রেপো:

https://github.com/ThornTechPublic/InteractiveModal

এটিই আমি ব্যবহার করেছি:

নিয়ামক দেখুন

আপনি একটি কাস্টম দিয়ে বরখাস্ত অ্যানিমেশনটিকে ওভাররাইড করুন। যদি ব্যবহারকারী মোডালটি টানছেন তবে interactorকিক্স ইন।

import UIKit

class ViewController: UIViewController {
    let interactor = Interactor()
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if let destinationViewController = segue.destinationViewController as? ModalViewController {
            destinationViewController.transitioningDelegate = self
            destinationViewController.interactor = interactor
        }
    }
}

extension ViewController: UIViewControllerTransitioningDelegate {
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissAnimator()
    }
    func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactor.hasStarted ? interactor : nil
    }
}

অ্যানিম্যাটর খারিজ করুন

আপনি একটি কাস্টম অ্যানিমেটার তৈরি করুন। এটি একটি কাস্টম অ্যানিমেশন যা আপনি কোনও UIViewControllerAnimatedTransitioningপ্রোটোকলের অভ্যন্তরে প্যাকেজ করেন ।

import UIKit

class DismissAnimator : NSObject {
}

extension DismissAnimator : UIViewControllerAnimatedTransitioning {
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return 0.6
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        guard
            let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
            let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey),
            let containerView = transitionContext.containerView()
            else {
                return
        }
        containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
        let screenBounds = UIScreen.mainScreen().bounds
        let bottomLeftCorner = CGPoint(x: 0, y: screenBounds.height)
        let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size)

        UIView.animateWithDuration(
            transitionDuration(transitionContext),
            animations: {
                fromVC.view.frame = finalFrame
            },
            completion: { _ in
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            }
        )
    }
}

ইন্টারেক্টর

আপনি সাবক্লাস করেছেন UIPercentDrivenInteractiveTransitionযাতে এটি আপনার রাষ্ট্রের মেশিন হিসাবে কাজ করতে পারে। যেহেতু ইন্টারেক্টর বস্তুটি উভয় ভিসি দ্বারা অ্যাক্সেস করা হয়েছে, প্যানিংয়ের অগ্রগতি ট্র্যাক রাখতে এটি ব্যবহার করুন।

import UIKit

class Interactor: UIPercentDrivenInteractiveTransition {
    var hasStarted = false
    var shouldFinish = false
}

মডেল ভিউ কন্ট্রোলার

এটি ইন্টারঅ্যাক্টর পদ্ধতি কলগুলিতে প্যান অঙ্গভঙ্গির অবস্থার মানচিত্র করে। translationInView() yমান নির্ধারণ করে ব্যবহারকারী একটি থ্রেশহোল্ড অতিক্রম কিনা। যখন প্যানের অঙ্গভঙ্গি হয় .Ended, তখন ইন্টারেক্টর হয় শেষ করে দেয় বা বাতিল করে দেয়।

import UIKit

class ModalViewController: UIViewController {

    var interactor:Interactor? = nil

    @IBAction func close(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }

    @IBAction func handleGesture(sender: UIPanGestureRecognizer) {
        let percentThreshold:CGFloat = 0.3

        // convert y-position to downward pull progress (percentage)
        let translation = sender.translationInView(view)
        let verticalMovement = translation.y / view.bounds.height
        let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
        let downwardMovementPercent = fminf(downwardMovement, 1.0)
        let progress = CGFloat(downwardMovementPercent)
        guard let interactor = interactor else { return }

        switch sender.state {
        case .Began:
            interactor.hasStarted = true
            dismissViewControllerAnimated(true, completion: nil)
        case .Changed:
            interactor.shouldFinish = progress > percentThreshold
            interactor.updateInteractiveTransition(progress)
        case .Cancelled:
            interactor.hasStarted = false
            interactor.cancelInteractiveTransition()
        case .Ended:
            interactor.hasStarted = false
            interactor.shouldFinish
                ? interactor.finishInteractiveTransition()
                : interactor.cancelInteractiveTransition()
        default:
            break
        }
    }

}

4
আরে রবার্ট, দুর্দান্ত কাজ আপনার কী ধারণা আছে যে কীভাবে আমরা এটির এটিকে টেবিল দর্শন দিয়ে কাজ করার অনুমতি দিতে সংশোধন করতে পারি? তা হচ্ছে, টেবিলভিউ শীর্ষে থাকলে বরখাস্ত করতে টানতে সক্ষম হবেন? ধন্যবাদ
রস বার্বিশ

4
রস, আমি একটি নতুন শাখা তৈরি করেছি যার কার্যকরী উদাহরণ রয়েছে: github.com/ThornTechPublic/InteractiveModal/tree/Ross । যদি প্রথমে দেখতে দেখতে এটি দেখতে চান তবে এই জিআইএফটি দেখুন : Raw.githubusercontent.com/ThornTechPublic/InteractiveModal/… । সারণী দর্শনটিতে একটি অন্তর্নির্মিত প্যানস্টেচারআর সনাক্তকারী রয়েছে যা লক্ষ্য-অ্যাকশনের মাধ্যমে বিদ্যমান হ্যান্ডেল ইঙ্গিত (_ :) পদ্ধতিতে তারযুক্ত হতে পারে। সাধারণ টেবিলের স্ক্রোলিংয়ের সাথে দ্বন্দ্ব এড়াতে, টেবিলটি শীর্ষে স্ক্রোল করা হয় কেবল তখনই টান-ডাউন বরখাস্তে লাথি মারা হয়। আমি একটি স্ন্যাপশট ব্যবহার করেছি এবং পাশাপাশি প্রচুর মন্তব্যও যুক্ত করেছি।
রবার্ট চেন

রবার্ট, আরও দুর্দান্ত কাজ। আমি নিজস্ব বাস্তবায়ন করেছি যা বিদ্যমান টেবিলভিউ প্যানিং পদ্ধতিগুলি যেমন স্ক্রোলভিউডিডসক্রল, স্ক্রোলভিউ উইলব্যাগিনড্র্যাগিং ব্যবহার করে uses এটির জন্য টেবিলভিউয়ের বাউন্স এবং বাউন্স হওয়া দরকার ভার্চিক্যালি উভয়ই সত্য হয়ে থাকে - এইভাবে আমরা টেবিলভিউ আইটেমগুলির কন্টেন্টঅফसेट পরিমাপ করতে পারি। এই পদ্ধতির সুবিধা হ'ল পর্যাপ্ত বেগ (বাউন্সিংয়ের কারণে) থাকলে টেবিলভিউটিকে একটি অঙ্গভঙ্গিতে স্ক্রিন থেকে সরিয়ে দেওয়া সম্ভব বলে মনে হচ্ছে। আমি সম্ভবত আপনাকে এই সপ্তাহে কিছুটা সময় টানতে অনুরোধ পাঠাব, উভয় বিকল্পই বৈধ বলে মনে হচ্ছে।
রস বার্বিশ

ভালো কাজ @ রোসবার্বিশ, আপনি কীভাবে তা টানলেন তা দেখার জন্য আমি অপেক্ষা করতে পারি না। এটি স্ক্রোল আপ করতে সক্ষম হতে বেশ মিষ্টি হবে এবং তারপরে ইন্টারেক্টিভ ট্রানজিশন মোডে প্রবেশ করুন, সমস্তই এক তরল গতিতে।
রবার্ট চেন

4
পিছনে কালো পর্দা এড়ানোর segueজন্য উপস্থাপনা সম্পত্তি সেট করুন over current contextযখন আপনি ভিউকন্ট্রোলারটি নীচে টানবেন
নিতিশ005

63

আমি সুইফট 3 এ এটি কীভাবে করেছি তা ভাগ করব:

ফলাফল

বাস্তবায়ন

class MainViewController: UIViewController {

  @IBAction func click() {
    performSegue(withIdentifier: "showModalOne", sender: nil)
  }
  
}

class ModalOneViewController: ViewControllerPannable {
  override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .yellow
  }
  
  @IBAction func click() {
    performSegue(withIdentifier: "showModalTwo", sender: nil)
  }
}

class ModalTwoViewController: ViewControllerPannable {
  override func viewDidLoad() {
    super.viewDidLoad()
    
    view.backgroundColor = .green
  }
}

যেখানে মডেল ভিউ কন্ট্রোলাররা নির্দিষ্ট গতিতে পৌঁছানোর পরে এগুলিকে টেনে আনে এবং খারিজযোগ্য করে তোলার জন্য classআমি তৈরি করেছি fromViewControllerPannable

ভিউকন্ট্রোলারপ্যানিয়েবল ক্লাস

class ViewControllerPannable: UIViewController {
  var panGestureRecognizer: UIPanGestureRecognizer?
  var originalPosition: CGPoint?
  var currentPositionTouched: CGPoint?
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureAction(_:)))
    view.addGestureRecognizer(panGestureRecognizer!)
  }
  
  func panGestureAction(_ panGesture: UIPanGestureRecognizer) {
    let translation = panGesture.translation(in: view)
    
    if panGesture.state == .began {
      originalPosition = view.center
      currentPositionTouched = panGesture.location(in: view)
    } else if panGesture.state == .changed {
        view.frame.origin = CGPoint(
          x: translation.x,
          y: translation.y
        )
    } else if panGesture.state == .ended {
      let velocity = panGesture.velocity(in: view)

      if velocity.y >= 1500 {
        UIView.animate(withDuration: 0.2
          , animations: {
            self.view.frame.origin = CGPoint(
              x: self.view.frame.origin.x,
              y: self.view.frame.size.height
            )
          }, completion: { (isCompleted) in
            if isCompleted {
              self.dismiss(animated: false, completion: nil)
            }
        })
      } else {
        UIView.animate(withDuration: 0.2, animations: {
          self.view.center = self.originalPosition!
        })
      }
    }
  }
}

4
আমি আপনার কোডটি অনুলিপি করেছি এবং এটি কার্যকর হয়েছে। মডেল ভিউয়ের পটভূমি যখন নীচে টানুন কালো, আপনার মতো স্বচ্ছ নয়
Anh

4
স্টোরিবোর্ড থেকে ইন্সপেক্টর আরোপ প্যানেলের Storyboard segueএর MainViewControllerথেকে ModalViewControllerসেট করুন: উপস্থাপনা করার সম্পত্তি ওভার বর্তমান প্রসঙ্গ
উইলসন

স্বীকৃত উত্তরটি এটি সহজ বলে মনে হচ্ছে তবে আমি ভিউকন্ট্রোলারপ্যানিয়েবল শ্রেণিতে একটি ত্রুটি পাচ্ছি। ত্রুটিটি হ'ল "নন-ফাংশন প্রকারের UIPanGestureRecognizer এর মান কল করতে পারে না"। এটি "PanGestureRecognizer = UIPanGestureRecognizer (টার্গেট: স্ব, ক্রিয়া: # নির্বাচক (PanGestureAction (_ :))) লাইনে রয়েছে" কোনও ধারণা?
tylerSF

আমি উল্লিখিত ত্রুটিটি ইউআইপিএনগাস্টারআর সনাক্তকারীকে পরিবর্তন করে স্থির করেছিলাম "panGestureRecognizer = panGestureRecognizer (টার্গেট ..." এ পরিবর্তিত হয়েছে: "panGestureRecognizer = UIPanGestureRecognizer (টার্গেট ..."
টাইলারএসএফ

যেহেতু আমি উপাচার্যকে উপস্থাপন করছি এটি আধুনিকভাবে উপস্থাপন করছে না, তাই আমি কীভাবে বরখাস্ত করার সময় কালো পটভূমি সরিয়ে ফেলতে পারি?
মিঃ বিন বিন

20

নীচের উন্নতিগুলির সাথে @ উইলসনের উত্তর (ধন্যবাদ 👍) এর উপর ভিত্তি করে এখানে একটি ফাইল সমাধান রয়েছে:


পূর্ববর্তী সমাধান থেকে উন্নতির তালিকা

  • প্যানিং সীমাবদ্ধ করুন যাতে দৃশ্যটি কেবল নীচে যায়:
    • শুধুমাত্র এর yসমন্বয় আপডেট করে অনুভূমিক অনুবাদ এড়ানview.frame.origin
    • সাথে স্যুইপ করার সময় পর্দা থেকে প্যানেল এড়িয়ে চলুন let y = max(0, translation.y)
  • আঙুলটি কোথায় প্রকাশিত হবে তার ভিত্তিতে ভিউ কন্ট্রোলারকে বরখাস্ত করুন (পর্দার নীচের অর্ধেক ডিফল্ট) এবং কেবল সোয়াইপের বেগের উপর ভিত্তি করে নয়
  • পূর্ববর্তী ভিউকন্ট্রোলার পিছনে উপস্থিত রয়েছে তা নিশ্চিত করার জন্য ভিউ কন্ট্রোলারটি মডেল হিসাবে দেখান এবং একটি কালো পটভূমি এড়ানো (আপনার প্রশ্নের উত্তর দেওয়া উচিত @ nguy ann-anh-việt)
  • অপরিবর্তিত currentPositionTouchedএবং সরানoriginalPosition
  • নিম্নলিখিত পরামিতি প্রকাশ করুন:
    • minimumVelocityToHide: গতিটি লুকানোর জন্য যথেষ্ট (1500 ডিফল্ট)
    • minimumScreenRatioToHide: আড়াল করার জন্য কত কম যথেষ্ট (ডিফল্ট ০.৫ এ)
    • animationDuration : আমরা কীভাবে দ্রুত / লুকিয়ে থাকি (0.2 এর ডিফল্টে)

সমাধান

সুইফট 3 এবং সুইফট 4:

//
//  PannableViewController.swift
//

import UIKit

class PannableViewController: UIViewController {
    public var minimumVelocityToHide: CGFloat = 1500
    public var minimumScreenRatioToHide: CGFloat = 0.5
    public var animationDuration: TimeInterval = 0.2

    override func viewDidLoad() {
        super.viewDidLoad()

        // Listen for pan gesture
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(onPan(_:)))
        view.addGestureRecognizer(panGesture)
    }

    @objc func onPan(_ panGesture: UIPanGestureRecognizer) {

        func slideViewVerticallyTo(_ y: CGFloat) {
            self.view.frame.origin = CGPoint(x: 0, y: y)
        }

        switch panGesture.state {

        case .began, .changed:
            // If pan started or is ongoing then
            // slide the view to follow the finger
            let translation = panGesture.translation(in: view)
            let y = max(0, translation.y)
            slideViewVerticallyTo(y)

        case .ended:
            // If pan ended, decide it we should close or reset the view
            // based on the final position and the speed of the gesture
            let translation = panGesture.translation(in: view)
            let velocity = panGesture.velocity(in: view)
            let closing = (translation.y > self.view.frame.size.height * minimumScreenRatioToHide) ||
                          (velocity.y > minimumVelocityToHide)

            if closing {
                UIView.animate(withDuration: animationDuration, animations: {
                    // If closing, animate to the bottom of the view
                    self.slideViewVerticallyTo(self.view.frame.size.height)
                }, completion: { (isCompleted) in
                    if isCompleted {
                        // Dismiss the view when it dissapeared
                        dismiss(animated: false, completion: nil)
                    }
                })
            } else {
                // If not closing, reset the view to the top
                UIView.animate(withDuration: animationDuration, animations: {
                    slideViewVerticallyTo(0)
                })
            }

        default:
            // If gesture state is undefined, reset the view to the top
            UIView.animate(withDuration: animationDuration, animations: {
                slideViewVerticallyTo(0)
            })

        }
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)   {
        super.init(nibName: nil, bundle: nil)
        modalPresentationStyle = .overFullScreen;
        modalTransitionStyle = .coverVertical;
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        modalPresentationStyle = .overFullScreen;
        modalTransitionStyle = .coverVertical;
    }
}

আপনার কোডে হয় টাইপো বা একটি অনুপস্থিত ভেরিয়েবল "ন্যূনতম হাইটারটিওটোহাইড" রয়েছে
শোকাভেলি

4
ধন্যবাদ @ শোকাভেলি, স্থির (ছিলেন minimumScreenRatioToHide)
এগ্রিওল্ট Agirault

এটি একটি খুব সুন্দর সমাধান। তবে, আমার একটি ছোট সমস্যা রয়েছে এবং কারণটি কী তা সম্পর্কে পুরোপুরি নিশ্চিত নই: ড্রপবক্স / এস / 77abbkl9vh2goif8 / pannable.gif?dl=0 লাল ব্যাকগ্রাউন্ডটি মডেলের ভিসির একটি অংশ, নীল পটভূমিটি ভিসির অংশ মডেল উপস্থাপন। প্যান অঙ্গভঙ্গি শনাক্তকারী যখন শুরু হয় তখন আমি এই চটকদার আচরণটি ঠিক করেছি বলে মনে হচ্ছে না।
নোলরাপ

4
হাই @ ননলরাপ self.view.frame.originআপনি sliceViewVerticallyToপ্রথমবার কল করার আগে এর মানটির দিকে নজর দিন : আমরা দেখতে পাই অফসেটটি স্ট্যাটাস বারের উচ্চতার সমান, তাই সম্ভবত আপনার প্রাথমিক উত্স 0 নয়?
আগ্রাসন

4
এটি slideViewVerticallyToনেস্টেড ফাংশন হিসাবে ব্যবহার করা ভাল onPan
নিক কোভ

16

স্ন্যাপচ্যাট এর আবিষ্কার মোডের মতো ভিউ কন্ট্রোলারকে বরখাস্ত করার জন্য ইন্টারেক্টিভভাবে নীচে টেনে আনার জন্য একটি ডেমো তৈরি করেছে। নমুনা প্রকল্পের জন্য এই গিথুবটি পরীক্ষা করুন ।

এখানে চিত্র বর্ণনা লিখুন


4
দুর্দান্ত, তবে এটি সত্যই পুরানো। কেউ কি অন্য একটি নমুনা প্রকল্প জানেন?
16:58 এ 12 এয়ারলাইনার

14

Pangesture ব্যবহার করে সুইফট 4.x x

সহজ উপায়

উল্লম্ব

class ViewConrtoller: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onDrage(_:))))
    }

    @objc func onDrage(_ sender:UIPanGestureRecognizer) {
        let percentThreshold:CGFloat = 0.3
        let translation = sender.translation(in: view)

        let newX = ensureRange(value: view.frame.minX + translation.x, minimum: 0, maximum: view.frame.maxX)
        let progress = progressAlongAxis(newX, view.bounds.width)

        view.frame.origin.x = newX //Move view to new position

        if sender.state == .ended {
            let velocity = sender.velocity(in: view)
           if velocity.x >= 300 || progress > percentThreshold {
               self.dismiss(animated: true) //Perform dismiss
           } else {
               UIView.animate(withDuration: 0.2, animations: {
                   self.view.frame.origin.x = 0 // Revert animation
               })
          }
       }

       sender.setTranslation(.zero, in: view)
    }
}

সহায়ক ফাংশন

func progressAlongAxis(_ pointOnAxis: CGFloat, _ axisLength: CGFloat) -> CGFloat {
        let movementOnAxis = pointOnAxis / axisLength
        let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0)
        let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0)
        return CGFloat(positiveMovementOnAxisPercent)
    }

    func ensureRange<T>(value: T, minimum: T, maximum: T) -> T where T : Comparable {
        return min(max(value, minimum), maximum)
    }

কঠিন পথ

এটি উল্লেখ করুন -> https://github.com/satishVekariya/DraggableViewController


4
আমি আপনার কোড ব্যবহার করার চেষ্টা করেছি। তবে আমি আমার সামান্য পর্যালোচনা নীচে ছিল এবং ব্যবহারকারী যখন দৃশ্য টেনে আনেন তখন টেপযুক্ত অবস্থানের সাথে সাবউউয়ের উচ্চতাও বাড়ানো উচিত। দ্রষ্টব্য: - অঙ্গভঙ্গি ইভেন্টটি এটি পর্যবেক্ষণে রাখা হয়েছে
একরা

অবশ্যই, আপনি এটি করতে পারেন
স্প্যাটেল

হাই @ স্ট্যাটেল এক্স অক্ষের বাম দিকে টানতে এই কোডটি কীভাবে পরিবর্তন করতে হবে সে সম্পর্কে কোনও ধারণা, অর্থাত্, অক্ষ অক্ষের সাথে নেতিবাচক গতিবিধি?
নিখিল পান্ডে

4
@ স্ট্যাটেল আপনাকে উত্তরের শিরোনামটিও পরিবর্তন করতে হবে যেহেতু উল্লম্বভাবে এক্স অক্ষের গতিবিধি প্রদর্শিত হচ্ছে এবং অনুভূমিক ওয়াই অক্ষের গতিবিধি প্রদর্শন করছে।
নিখিল পান্ডে

4
modalPresentationStyle = UIModalPresentationOverFullScreenপিছনে পর্দার পিছনে এড়ানোর জন্য সেট করা স্মৃতিচিহ্ন view
Tounaobun

13

এটি করার জন্য আমি খুব সহজ উপায় খুঁজে পেয়েছি। আপনার ভিউ কন্ট্রোলারে কেবল নিম্নলিখিত কোডটি লিখুন:

সুইফট 4

override func viewDidLoad() {
    super.viewDidLoad()
    let gestureRecognizer = UIPanGestureRecognizer(target: self,
                                                   action: #selector(panGestureRecognizerHandler(_:)))
    view.addGestureRecognizer(gestureRecognizer)
}

@IBAction func panGestureRecognizerHandler(_ sender: UIPanGestureRecognizer) {
    let touchPoint = sender.location(in: view?.window)
    var initialTouchPoint = CGPoint.zero

    switch sender.state {
    case .began:
        initialTouchPoint = touchPoint
    case .changed:
        if touchPoint.y > initialTouchPoint.y {
            view.frame.origin.y = touchPoint.y - initialTouchPoint.y
        }
    case .ended, .cancelled:
        if touchPoint.y - initialTouchPoint.y > 200 {
            dismiss(animated: true, completion: nil)
        } else {
            UIView.animate(withDuration: 0.2, animations: {
                self.view.frame = CGRect(x: 0,
                                         y: 0,
                                         width: self.view.frame.size.width,
                                         height: self.view.frame.size.height)
            })
        }
    case .failed, .possible:
        break
    }
}

4
ধন্যবাদ, পুরোপুরি কাজ করে! ইন্টারফেস বিল্ডারে দেখার জন্য কেবল একটি প্যান গেসচার রিকগনিজারকে ফেলে দিন এবং উপরের @ আইবিএક્શનের সাথে সংযুক্ত হন।
বালাজস 630

সুইফট 5 এও কাজ করে। @ বালাজস 630 যে নির্দেশনা দিয়েছেন তা কেবল অনুসরণ করুন।
লন্ডনগুই

আমি মনে করি এটি সেরা উপায়।
এনেখা

@ অ্যালেক্স শুবিন, ভিউকন্ট্রোলার থেকে টববারকন্ট্রোলারে টেনে আনলে কীভাবে বরখাস্ত করবেন?
ভাদলপল্লী মাষ্টান

12

সুইফট 4-র জন্য প্রচুর পরিমাণে রেপো আপডেট করে ।

জন্য সুইফট 3 , আমি বর্তমানে একটি নিম্নলিখিত তৈরি করেছেন UIViewControllerডান দিক থেকে বাম এবং প্যান অঙ্গভঙ্গি দ্বারা এটি খারিজ করতে। আমি এটি একটি গিটহাব সংগ্রহশালা হিসাবে আপলোড করেছি ।

এখানে চিত্র বর্ণনা লিখুন

DismissOnPanGesture.swift ফাইল:

//  Created by David Seek on 11/21/16.
//  Copyright © 2016 David Seek. All rights reserved.

import UIKit

class DismissAnimator : NSObject {
}

extension DismissAnimator : UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.6
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        let screenBounds = UIScreen.main.bounds
        let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
        let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
        var x:CGFloat      = toVC!.view.bounds.origin.x - screenBounds.width
        let y:CGFloat      = toVC!.view.bounds.origin.y
        let width:CGFloat  = toVC!.view.bounds.width
        let height:CGFloat = toVC!.view.bounds.height
        var frame:CGRect   = CGRect(x: x, y: y, width: width, height: height)

        toVC?.view.alpha = 0.2

        toVC?.view.frame = frame
        let containerView = transitionContext.containerView

        containerView.insertSubview(toVC!.view, belowSubview: fromVC!.view)


        let bottomLeftCorner = CGPoint(x: screenBounds.width, y: 0)
        let finalFrame = CGRect(origin: bottomLeftCorner, size: screenBounds.size)

        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            animations: {
                fromVC!.view.frame = finalFrame
                toVC?.view.alpha = 1

                x = toVC!.view.bounds.origin.x
                frame = CGRect(x: x, y: y, width: width, height: height)

                toVC?.view.frame = frame
            },
            completion: { _ in
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        )
    }
}

class Interactor: UIPercentDrivenInteractiveTransition {
    var hasStarted = false
    var shouldFinish = false
}

let transition: CATransition = CATransition()

func presentVCRightToLeft(_ fromVC: UIViewController, _ toVC: UIViewController) {
    transition.duration = 0.5
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromRight
    fromVC.view.window!.layer.add(transition, forKey: kCATransition)
    fromVC.present(toVC, animated: false, completion: nil)
}

func dismissVCLeftToRight(_ vc: UIViewController) {
    transition.duration = 0.5
    transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    transition.type = kCATransitionPush
    transition.subtype = kCATransitionFromLeft
    vc.view.window!.layer.add(transition, forKey: nil)
    vc.dismiss(animated: false, completion: nil)
}

func instantiatePanGestureRecognizer(_ vc: UIViewController, _ selector: Selector) {
    var edgeRecognizer: UIScreenEdgePanGestureRecognizer!
    edgeRecognizer = UIScreenEdgePanGestureRecognizer(target: vc, action: selector)
    edgeRecognizer.edges = .left
    vc.view.addGestureRecognizer(edgeRecognizer)
}

func dismissVCOnPanGesture(_ vc: UIViewController, _ sender: UIScreenEdgePanGestureRecognizer, _ interactor: Interactor) {
    let percentThreshold:CGFloat = 0.3
    let translation = sender.translation(in: vc.view)
    let fingerMovement = translation.x / vc.view.bounds.width
    let rightMovement = fmaxf(Float(fingerMovement), 0.0)
    let rightMovementPercent = fminf(rightMovement, 1.0)
    let progress = CGFloat(rightMovementPercent)

    switch sender.state {
    case .began:
        interactor.hasStarted = true
        vc.dismiss(animated: true, completion: nil)
    case .changed:
        interactor.shouldFinish = progress > percentThreshold
        interactor.update(progress)
    case .cancelled:
        interactor.hasStarted = false
        interactor.cancel()
    case .ended:
        interactor.hasStarted = false
        interactor.shouldFinish
            ? interactor.finish()
            : interactor.cancel()
    default:
        break
    }
}

সহজ ব্যবহার:

import UIKit

class VC1: UIViewController, UIViewControllerTransitioningDelegate {

    let interactor = Interactor()

    @IBAction func present(_ sender: Any) {
        let vc = self.storyboard?.instantiateViewController(withIdentifier: "VC2") as! VC2
        vc.transitioningDelegate = self
        vc.interactor = interactor

        presentVCRightToLeft(self, vc)
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissAnimator()
    }

    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return interactor.hasStarted ? interactor : nil
    }
}

class VC2: UIViewController {

    var interactor:Interactor? = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        instantiatePanGestureRecognizer(self, #selector(gesture))
    }

    @IBAction func dismiss(_ sender: Any) {
        dismissVCLeftToRight(self)
    }

    func gesture(_ sender: UIScreenEdgePanGestureRecognizer) {
        dismissVCOnPanGesture(self, sender, interactor!)
    }
}

4
তুলনাহীন! ভাগ করে নেওয়ার জন্য ধন্যবাদ.
শারদ চৌহান

হাই, আমি কীভাবে প্যান অঙ্গভঙ্গি
সনাক্তকারী

আমি এখনই টিউটোরিয়াল দিয়ে একটি ইউটিউব চ্যানেল শুরু করছি। আমি iOS 13+ / সুইফট 5
ডেভিড সিক

6

আপনি যা বর্ণনা করছেন তা হলেন একটি ইন্টারেক্টিভ কাস্টম ট্রানজিশন অ্যানিমেশন । আপনি অ্যানিমেশন এবং একটি রূপান্তরটির ড্রাইভিং অঙ্গভঙ্গি উভয়ই কাস্টমাইজ করছেন, অর্থাত্ উপস্থাপিত ভিউ কন্ট্রোলারের বরখাস্ত (বা না)। এটি বাস্তবায়নের সহজতম উপায় হ'ল একটি ইউআইপিএনসেন্টারআরেক্টিগাইজারকে একটি ইউআইপিআরসেন্টড্রাইভেন ইন্টারেক্টিভ ট্রান্সশিশনের সাথে সংযুক্ত করে।

আমার বই এটি কীভাবে করবেন তা ব্যাখ্যা করে এবং আমি উদাহরণগুলি (বই থেকে) পোস্ট করেছি। এই বিশেষ উদাহরণটি একটি পৃথক পরিস্থিতি - রূপান্তরটি পাশের দিকে, নীচে নয়, এবং এটি কোনও ট্যাব বার নিয়ামকের জন্য, উপস্থাপিত নিয়ামক নয় - তবে মূল ধারণাটি হুবহু:

https://github.com/mattneub/Pramramming-iOS-Book- উদাহরণ / ব্লব / মাস্টার/bk2ch06p296customAnimation2/ch19p620customAnimation1/appDelegate.swift

আপনি যদি এই প্রকল্পটি ডাউনলোড করে চালিয়ে যান তবে আপনি যা দেখতে পাচ্ছেন তা হ'ল যা আপনি বর্ণনা করছেন ঠিক তা পাশের রাস্তা বাদে: যদি টানা অর্ধেকের বেশি হয় তবে আমরা স্থানান্তর করি, তবে যদি তা না হয় তবে আমরা বাতিল করে ফিরি এবং তার মধ্যে ফিরে আসি into স্থান।


4
404 পৃষ্ঠা খুঁজে পাওয়া যায়নি.
ট্র্যাপার

6

কেবল উল্লম্ব বরখাস্ত

func panGestureAction(_ panGesture: UIPanGestureRecognizer) {
    let translation = panGesture.translation(in: view)

    if panGesture.state == .began {
        originalPosition = view.center
        currentPositionTouched = panGesture.location(in: view)    
    } else if panGesture.state == .changed {
        view.frame.origin = CGPoint(
            x:  view.frame.origin.x,
            y:  view.frame.origin.y + translation.y
        )
        panGesture.setTranslation(CGPoint.zero, in: self.view)
    } else if panGesture.state == .ended {
        let velocity = panGesture.velocity(in: view)
        if velocity.y >= 150 {
            UIView.animate(withDuration: 0.2
                , animations: {
                    self.view.frame.origin = CGPoint(
                        x: self.view.frame.origin.x,
                        y: self.view.frame.size.height
                    )
            }, completion: { (isCompleted) in
                if isCompleted {
                    self.dismiss(animated: false, completion: nil)
                }
            })
        } else {
            UIView.animate(withDuration: 0.2, animations: {
                self.view.center = self.originalPosition!
            })
        }
    }

6

আমি এক্সটেনশন ব্যবহার করার সহজ তৈরি করেছি।

শুধু সহজাত InteractiveViewController এর সাথে আপনার UIViewController এবং আপনার কাজ সম্পন্ন হয় InteractiveViewController

ইন্টারেক্টিভ হিসাবে দেখানোর জন্য আপনার নিয়ামক থেকে কল পদ্ধতি শো-ইন্টারেক্টিভ ()।

এখানে চিত্র বর্ণনা লিখুন


4

উদ্দেশ্য সি তে: এখানে কোড

ভিতরেviewDidLoad

UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc]
                                             initWithTarget:self action:@selector(swipeDown:)];
swipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
[self.view addGestureRecognizer:swipeRecognizer];

//Swipe Down Method

- (void)swipeDown:(UIGestureRecognizer *)sender{
[self dismissViewControllerAnimated:YES completion:nil];
}

সোয়াইপ করার সময়টিকে বরখাস্ত করার আগে কীভাবে নিয়ন্ত্রণ করতে হবে?
টনিটনি

2

@ উইলসনের উত্তরের উপর ভিত্তি করে আমি এখানে তৈরি করেছি

// MARK: IMPORT STATEMENTS
import UIKit

// MARK: EXTENSION
extension UIViewController {

    // MARK: IS SWIPABLE - FUNCTION
    func isSwipable() {
        let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))
        self.view.addGestureRecognizer(panGestureRecognizer)
    }

    // MARK: HANDLE PAN GESTURE - FUNCTION
    @objc func handlePanGesture(_ panGesture: UIPanGestureRecognizer) {
        let translation = panGesture.translation(in: view)
        let minX = view.frame.width * 0.135
        var originalPosition = CGPoint.zero

        if panGesture.state == .began {
            originalPosition = view.center
        } else if panGesture.state == .changed {
            view.frame.origin = CGPoint(x: translation.x, y: 0.0)

            if panGesture.location(in: view).x > minX {
                view.frame.origin = originalPosition
            }

            if view.frame.origin.x <= 0.0 {
                view.frame.origin.x = 0.0
            }
        } else if panGesture.state == .ended {
            if view.frame.origin.x >= view.frame.width * 0.5 {
                UIView.animate(withDuration: 0.2
                     , animations: {
                        self.view.frame.origin = CGPoint(
                            x: self.view.frame.size.width,
                            y: self.view.frame.origin.y
                        )
                }, completion: { (isCompleted) in
                    if isCompleted {
                        self.dismiss(animated: false, completion: nil)
                    }
                })
            } else {
                UIView.animate(withDuration: 0.2, animations: {
                    self.view.frame.origin = originalPosition
                })
            }
        }
    }

}

ব্যবহার

আপনার ভিউ কন্ট্রোলারের অভ্যন্তরে আপনি সোয়াইপ করতে চান:

override func viewDidLoad() {
    super.viewDidLoad()

    self.isSwipable()
}

এবং এটিকে নেভিগেশন নিয়ামক হিসাবে ভিউ কন্ট্রোলারের চরম বাম দিক থেকে সোয়াইপ করে বর্জনযোগ্য হবে।


আরে, আমি আপনার কোড ব্যবহার করেছি এবং এটি ডান সোয়াইপের জন্য পুরোপুরি কাজ করে তবে আমি সোয়াইপ ডাউন করে খারিজ করতে চাই, আমি এটি কীভাবে করব? সাহায্য করুন!
খুশ

2

অক্ষ থেকে ভিউকন্ট্রোলার টেনে আনতে এটি আমার সহজ বর্গ । শুধু herited DraggableViewController থেকে আপনার বর্গ।

MyCustomClass: DraggableViewController

কেবল উপস্থাপিত ভিউকন্ট্রোলারের জন্য কাজ করুন।

// MARK: - DraggableViewController

public class DraggableViewController: UIViewController {

    public let percentThresholdDismiss: CGFloat = 0.3
    public var velocityDismiss: CGFloat = 300
    public var axis: NSLayoutConstraint.Axis = .horizontal
    public var backgroundDismissColor: UIColor = .black {
        didSet {
            navigationController?.view.backgroundColor = backgroundDismissColor
        }
    }

    // MARK: LifeCycle

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(onDrag(_:))))
    }

    // MARK: Private methods

    @objc fileprivate func onDrag(_ sender: UIPanGestureRecognizer) {

        let translation = sender.translation(in: view)

        // Movement indication index
        let movementOnAxis: CGFloat

        // Move view to new position
        switch axis {
        case .vertical:
            let newY = min(max(view.frame.minY + translation.y, 0), view.frame.maxY)
            movementOnAxis = newY / view.bounds.height
            view.frame.origin.y = newY

        case .horizontal:
            let newX = min(max(view.frame.minX + translation.x, 0), view.frame.maxX)
            movementOnAxis = newX / view.bounds.width
            view.frame.origin.x = newX
        }

        let positiveMovementOnAxis = fmaxf(Float(movementOnAxis), 0.0)
        let positiveMovementOnAxisPercent = fminf(positiveMovementOnAxis, 1.0)
        let progress = CGFloat(positiveMovementOnAxisPercent)
        navigationController?.view.backgroundColor = UIColor.black.withAlphaComponent(1 - progress)

        switch sender.state {
        case .ended where sender.velocity(in: view).y >= velocityDismiss || progress > percentThresholdDismiss:
            // After animate, user made the conditions to leave
            UIView.animate(withDuration: 0.2, animations: {
                switch self.axis {
                case .vertical:
                    self.view.frame.origin.y = self.view.bounds.height

                case .horizontal:
                    self.view.frame.origin.x = self.view.bounds.width
                }
                self.navigationController?.view.backgroundColor = UIColor.black.withAlphaComponent(0)

            }, completion: { finish in
                self.dismiss(animated: true) //Perform dismiss
            })
        case .ended:
            // Revert animation
            UIView.animate(withDuration: 0.2, animations: {
                switch self.axis {
                case .vertical:
                    self.view.frame.origin.y = 0

                case .horizontal:
                    self.view.frame.origin.x = 0
                }
            })
        default:
            break
        }
        sender.setTranslation(.zero, in: view)
    }
}

2

যারা সত্যিকার অর্থে কাস্টম ইউআইভিউউকন্ট্রোলার ট্রানজিশনের আরও কিছুটা গভীরভাবে ডুব দিতে চান, তাদের জন্য আমি রায়ওয়েন্ডারলিচ ডটকম থেকে এই দুর্দান্ত টিউটোরিয়ালটি প্রস্তাব করছি

মূল চূড়ান্ত নমুনা প্রকল্পে বাগ রয়েছে। তাই আমি এটি স্থির করেছি এবং এটি গিথুব রেপোতে আপলোড করেছি । প্রকল্পটি সুইফট 5-এ রয়েছে, তাই আপনি সহজেই এটি চালাতে এবং খেলতে পারেন।

এখানে একটি পূর্বরূপ:

এবং এটি ইন্টারেক্টিভও!

শুভ হ্যাকিং!


0

ব্যবহারকারীর টানাকে সনাক্ত করতে এবং এটির সাথে মডেল ভিউটি সরানোর জন্য আপনি একটি ইউআইপিএনজেস্টারর সনাক্তকারী ব্যবহার করতে পারেন। যদি শেষের অবস্থানটি যথেষ্ট পরিমাণে নিচে থাকে তবে দৃশ্যটি বরখাস্ত করা যায়, বা অন্যথায় এনিমেটেডটিকে তার আসল অবস্থানে ফিরে যেতে পারে।

পরীক্ষা করে দেখুন এই উত্তর কিভাবে ভালো কিছু বাস্তবায়ন করতে আরও তথ্যের জন্য।

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