ฉันจะเลียนแบบแผ่นด้านล่างจากแอพ Maps ได้อย่างไร


202

มีใครบอกฉันได้ไหมว่าฉันจะเลียนแบบแผ่นด้านล่างในแอพ Maps ใหม่ใน iOS 10 ได้อย่างไร

ใน Android คุณสามารถใช้สิ่งBottomSheetนี้เลียนแบบพฤติกรรมนี้ได้ แต่ฉันไม่พบสิ่งเช่นนั้นสำหรับ iOS

นั่นคือมุมมองแบบเลื่อนที่เรียบง่ายพร้อมส่วนแทรกเนื้อหาเพื่อให้แถบการค้นหาอยู่ด้านล่าง?

ฉันค่อนข้างใหม่ในการเขียนโปรแกรม iOS ดังนั้นหากมีใครสามารถช่วยฉันสร้างเค้าโครงนี้ได้นั่นก็จะเป็นที่นิยมอย่างมาก

นี่คือสิ่งที่ฉันหมายถึงโดย "แผ่นด้านล่าง":

สกรีนช็อตของแผ่นด้านล่างที่ยุบลงในแผนที่

สกรีนช็อตของแผ่นด้านล่างที่ขยายออกในแผนที่


1
คุณต้องสร้างมันด้วยตัวเองฉันกลัว มันเป็นมุมมองที่มีมุมมองพื้นหลังเบลอและผู้รู้ท่าทางบางคน
Khanh Nguyen

@ KhanhNguyen ใช่ฉันรู้ว่าฉันต้องสร้างมันด้วยตัวเอง แต่สิ่งที่ฉันสงสัยคือมุมมองที่ใช้ในการรับผลกระทบนี้และวิธีที่พวกเขาได้รับคำสั่งและอื่น ๆ
qwertz

เป็นมุมมองเอฟเฟ็กต์ภาพพร้อมเอฟเฟกต์เบลอ ดูคำถาม SO
Khanh Nguyen

1
ฉันคิดว่าคุณสามารถเพิ่มตัวจดจำท่าทางเลื่อนไปที่มุมมองด้านล่างและย้ายมุมมองตามนั้น (โดยการสังเกตการเปลี่ยนแปลงโดยการเก็บพิกัด y ระหว่างเหตุการณ์ตัวจดจำท่าทางท่าทางและคำนวณความแตกต่าง)
Khanh Nguyen

2
ฉันชอบคำถามของคุณ ฉันวางแผนที่จะใช้สิ่งนี้ หากใครบางคนสามารถเขียนตัวอย่างจะดี
wviana

คำตอบ:


329

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

ฉันถือว่าคุณรู้วิธี:

1- สร้างตัวควบคุมมุมมองโดยกระดานเรื่องราวหรือใช้ไฟล์ xib

2- ใช้ googleMaps หรือ MapKit ของ Apple

ตัวอย่าง

1- 2 สร้างมุมมองตัวควบคุมเช่นMapViewControllerและBottomSheetViewController ตัวควบคุมแรกจะโฮสต์แผนที่และตัวที่สองคือแผ่นด้านล่าง

กำหนดค่า MapViewController

สร้างวิธีการเพิ่มมุมมองแผ่นด้านล่าง

func addBottomSheetView() {
    // 1- Init bottomSheetVC
    let bottomSheetVC = BottomSheetViewController()

    // 2- Add bottomSheetVC as a child view 
    self.addChildViewController(bottomSheetVC)
    self.view.addSubview(bottomSheetVC.view)
    bottomSheetVC.didMoveToParentViewController(self)

    // 3- Adjust bottomSheet frame and initial position.
    let height = view.frame.height
    let width  = view.frame.width
    bottomSheetVC.view.frame = CGRectMake(0, self.view.frame.maxY, width, height)
}

และเรียกมันในวิธี viewDidAppear:

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

กำหนดค่า BottomSheetViewController

1) เตรียมพื้นหลัง

สร้างวิธีการเพิ่มเอฟเฟ็กต์ภาพเบลอและภาพสั่นไหว

func prepareBackgroundView(){
    let blurEffect = UIBlurEffect.init(style: .Dark)
    let visualEffect = UIVisualEffectView.init(effect: blurEffect)
    let bluredView = UIVisualEffectView.init(effect: blurEffect)
    bluredView.contentView.addSubview(visualEffect)

    visualEffect.frame = UIScreen.mainScreen().bounds
    bluredView.frame = UIScreen.mainScreen().bounds

    view.insertSubview(bluredView, atIndex: 0)
}

เรียกวิธีนี้ใน viewWillAppear ของคุณ

override func viewWillAppear(animated: Bool) {
   super.viewWillAppear(animated)
   prepareBackgroundView()
}

ตรวจสอบให้แน่ใจว่าสีพื้นหลังมุมมองของคอนโทรลเลอร์ของคุณคือ clearColor

2) ภาพเคลื่อนไหวแผ่นชีทด้านล่างเคลื่อนไหว

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

    UIView.animateWithDuration(0.3) { [weak self] in
        let frame = self?.view.frame
        let yComponent = UIScreen.mainScreen().bounds.height - 200
        self?.view.frame = CGRectMake(0, yComponent, frame!.width, frame!.height)
    }
}

3) แก้ไข xib ของคุณตามที่คุณต้องการ

4) เพิ่ม Pan Gesture Recognizer ให้กับมุมมองของคุณ

ในวิธี viewDidLoad ของคุณให้เพิ่ม UIPanGestureRecognizer

override func viewDidLoad() {
    super.viewDidLoad()

    let gesture = UIPanGestureRecognizer.init(target: self, action: #selector(BottomSheetViewController.panGesture))
    view.addGestureRecognizer(gesture)

}

และใช้ลักษณะท่าทางสัมผัสของคุณ:

func panGesture(recognizer: UIPanGestureRecognizer) {
    let translation = recognizer.translationInView(self.view)
    let y = self.view.frame.minY
    self.view.frame = CGRectMake(0, y + translation.y, view.frame.width, view.frame.height)
     recognizer.setTranslation(CGPointZero, inView: self.view)
}

แผ่นด้านล่างเลื่อนได้:

หากมุมมองที่กำหนดเองของคุณคือมุมมองเลื่อนหรือมุมมองอื่น ๆ ที่สืบทอดมาคุณจึงมีสองตัวเลือก

ครั้งแรก:

ออกแบบมุมมองด้วยมุมมองส่วนหัวและเพิ่ม panGesture ให้กับส่วนหัว (ประสบการณ์การใช้งานที่ไม่ดี)

ประการที่สอง:

1 - เพิ่ม panGesture ไปยังมุมมองแผ่นด้านล่าง

2 - ใช้UIGestureRecognizerDelegateและตั้งค่ามอบสิทธิ์ panGesture ให้กับคอนโทรลเลอร์

3- การใช้งานควรรับรู้ทันทีพร้อมฟังก์ชั่นการมอบหมายและปิดการใช้งานคุณสมบัติscrollView isScrollEnabledในสองกรณี:

  • มุมมองสามารถมองเห็นได้บางส่วน
  • มุมมองสามารถมองเห็นได้ทั้งหมดคุณสมบัติscrollView contentOffsetคือ 0 และผู้ใช้กำลังลากมุมมองลงด้านล่าง

มิฉะนั้นเปิดใช้งานการเลื่อน

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
      let gesture = (gestureRecognizer as! UIPanGestureRecognizer)
      let direction = gesture.velocity(in: view).y

      let y = view.frame.minY
      if (y == fullView && tableView.contentOffset.y == 0 && direction > 0) || (y == partialView) {
          tableView.isScrollEnabled = false
      } else {
        tableView.isScrollEnabled = true
      }

      return false
  }

บันทึก

ในกรณีที่คุณตั้งค่า . allowUserInteractionเป็นตัวเลือกภาพเคลื่อนไหวเช่นในโครงการตัวอย่างดังนั้นคุณต้องเปิดใช้งานการเลื่อนในการปิดภาพเคลื่อนไหวหากผู้ใช้เลื่อนขึ้น

ตัวอย่างโครงการ

ฉันสร้างตัวอย่างโครงการที่มีตัวเลือกเพิ่มเติมในrepo นี้ซึ่งอาจช่วยให้คุณมีข้อมูลเชิงลึกที่ดีขึ้นเกี่ยวกับวิธีกำหนดโฟลว์

ในการสาธิตฟังก์ชัน addBottomSheetView () ควบคุมว่าควรใช้มุมมองใดเป็นแผ่นด้านล่าง

ภาพตัวอย่างโครงการ

- มุมมองบางส่วน

ป้อนคำอธิบายรูปภาพที่นี่

- FullView

ป้อนคำอธิบายรูปภาพที่นี่

- มุมมองที่เลื่อนได้

ป้อนคำอธิบายรูปภาพที่นี่


3
อันนี้เป็นแบบฝึกหัดที่ดีสำหรับเด็กวิวและย่อย ขอบคุณ :)
Edison

@ AhmedElassuty ฉันจะเพิ่ม tableview ใน xib และโหลดเป็นชีตล่างได้อย่างไรถ้าคุณสามารถช่วยได้จะดีมากขอบคุณล่วงหน้า!
nikhil nangia

3
@nikhilnangia ฉันเพิ่งอัพเดต repo ด้วย viewController อื่น "ScrollableBottomSheetViewController.swift" ที่มี tableView ฉันจะอัปเดตคำตอบโดยเร็วที่สุด ตรวจสอบเรื่องนี้ด้วยgithub.com/AhmedElassuty/IOS-BottomSheet/issues/1
Ahmad Elassuty

12
ฉันสังเกตเห็นว่าแผ่นด้านล่างของ Apple Maps จัดการการเปลี่ยนจากท่าทางลากของชีตเป็นรูปแบบการเลื่อนของมุมมองการเลื่อนในการเคลื่อนไหวที่ราบรื่นเพียงครั้งเดียวนั่นคือผู้ใช้ไม่จำเป็นต้องหยุดท่าทางเดียวและเริ่มใหม่เพื่อเริ่มเลื่อนมุมมอง ตัวอย่างของคุณไม่ได้ทำเช่นนี้ คุณมีความคิดเกี่ยวกับวิธีการที่จะประสบความสำเร็จหรือไม่? ขอบคุณและขอบคุณสำหรับการโพสต์ตัวอย่างใน repo!
Stoph

2
@ RafaelaLourençoฉันดีใจจริง ๆ ที่คุณคิดว่าคำตอบของฉันเป็นประโยชน์! ขอบคุณ!
Ahmad Elassuty

55

ฉันปล่อยห้องสมุดตามคำตอบด้านล่าง

มันเลียนแบบการซ้อนทับแอปพลิเคชันทางลัด ดูบทความนี้สำหรับรายละเอียด

OverlayContainerViewControllerองค์ประกอบหลักของห้องสมุดคือ มันกำหนดพื้นที่ที่ตัวควบคุมมุมมองสามารถลากขึ้นและลงซ่อนหรือเปิดเผยเนื้อหาภายใต้มัน

let contentController = MapsViewController()
let overlayController = SearchViewController()

let containerController = OverlayContainerViewController()
containerController.delegate = self
containerController.viewControllers = [
    contentController,
    overlayController
]

window?.rootViewController = containerController

ใช้OverlayContainerViewControllerDelegateเพื่อระบุจำนวนรอยหยักที่ต้องการ:

enum OverlayNotch: Int, CaseIterable {
    case minimum, medium, maximum
}

func numberOfNotches(in containerViewController: OverlayContainerViewController) -> Int {
    return OverlayNotch.allCases.count
}

func overlayContainerViewController(_ containerViewController: OverlayContainerViewController,
                                    heightForNotchAt index: Int,
                                    availableSpace: CGFloat) -> CGFloat {
    switch OverlayNotch.allCases[index] {
        case .maximum:
            return availableSpace * 3 / 4
        case .medium:
            return availableSpace / 2
        case .minimum:
            return availableSpace * 1 / 4
    }
}

คำตอบก่อนหน้า

ฉันคิดว่ามีประเด็นสำคัญที่ไม่ได้รับการแก้ไขในแนวทางแก้ไข: การเปลี่ยนระหว่างการเลื่อนและการแปล

แผนที่เปลี่ยนระหว่างการเลื่อนและการแปล

ในแผนที่อย่างที่คุณอาจสังเกตเห็นเมื่อ tableView มาถึงcontentOffset.y == 0แผ่นด้านล่างจะเลื่อนขึ้นหรือลง

จุดนี้เป็นเรื่องยุ่งยากเพราะเราไม่สามารถเปิด / ปิดการใช้งานการเลื่อนเมื่อท่าทางการแพนเริ่มการแปล มันจะหยุดการเลื่อนจนกว่าการสัมผัสใหม่จะเริ่มขึ้น นี่เป็นกรณีในโซลูชันส่วนใหญ่ที่นำเสนอที่นี่

นี่คือความพยายามของฉันที่จะใช้การเคลื่อนไหวนี้

จุดเริ่มต้น: แอป Maps

การเริ่มต้นการตรวจสอบของเราขอเห็นภาพลำดับชั้นมุมมองของแผนที่ (Maps บนเริ่มต้นจำลองและเลือกDebug> Attach to process by PID or Name> Mapsใน Xcode 9)

ลำดับชั้นมุมมองการดีบักของ Maps

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

กองควบคุมมุมมองของเรา

มาสร้างสถาปัตยกรรม Maps ViewController เวอร์ชั่นพื้นฐานกันเถอะ

เราเริ่มต้นด้วยBackgroundViewController(มุมมองแผนที่ของเรา):

class BackgroundViewController: UIViewController {
    override func loadView() {
        view = MKMapView()
    }
}

เราวาง tableView ไว้โดยเฉพาะUIViewController:

class OverlayViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    lazy var tableView = UITableView()

    override func loadView() {
        view = tableView
        tableView.dataSource = self
        tableView.delegate = self
    }

    [...]
}

ตอนนี้เราต้องการ VC เพื่อฝังภาพซ้อนทับและจัดการการแปล เพื่อลดความซับซ้อนของปัญหาเราพิจารณาว่าสามารถแปลโอเวอร์เลย์จากจุดคงที่หนึ่งOverlayPosition.maximumไปยังอีกจุดหนึ่งOverlayPosition.minimumได้

สำหรับตอนนี้มีเพียงวิธีการสาธารณะเพียงวิธีเดียวในการทำให้การเปลี่ยนแปลงตำแหน่งเปลี่ยนแปลงและมีมุมมองที่โปร่งใส:

enum OverlayPosition {
    case maximum, minimum
}

class OverlayContainerViewController: UIViewController {

    let overlayViewController: OverlayViewController
    var translatedViewHeightContraint = ...

    override func loadView() {
        view = UIView()
    }

    func moveOverlay(to position: OverlayPosition) {
        [...]
    }
}

ในที่สุดเราก็ต้องใช้ ViewController เพื่อฝังทั้งหมด:

class StackViewController: UIViewController {

    private var viewControllers: [UIViewController]

    override func viewDidLoad() {
        super.viewDidLoad()
        viewControllers.forEach { gz_addChild($0, in: view) }
    }
}

ใน AppDelegate ลำดับการเริ่มต้นของเรามีลักษณะดังนี้:

let overlay = OverlayViewController()
let containerViewController = OverlayContainerViewController(overlayViewController: overlay)
let backgroundViewController = BackgroundViewController()
window?.rootViewController = StackViewController(viewControllers: [backgroundViewController, containerViewController])

ความยากลำบากเบื้องหลังการแปลแบบซ้อนทับ

ตอนนี้วิธีการแปลภาพซ้อนทับของเรา?

โซลูชันที่นำเสนอส่วนใหญ่ใช้ตัวรู้จำท่าทางการเลื่อนเฉพาะ ยิ่งไปกว่านั้นเราต้องเก็บสโครลและการแปลให้ตรงกันและUIScrollViewDelegateมีกิจกรรมทั้งหมดที่เราต้องการ!

การใช้งานแบบไร้เดียงสาจะใช้ท่าทางที่สองของแพนและลองรีเซ็ตcontentOffsetมุมมองของตารางเมื่อการแปลเกิดขึ้น:

func panGestureAction(_ recognizer: UIPanGestureRecognizer) {
    if isTranslating {
        tableView.contentOffset = .zero
    }
}

แต่มันไม่ทำงาน tableView จะอัพเดตcontentOffsetเมื่อการทริกเกอร์การดำเนินการจดจำท่าทางด้วยตัวเองหรือเมื่อเรียกใช้ displayLink callback ไม่มีโอกาสที่ตัวจดจำของเราจะเรียกใช้หลังจากที่ผู้แทนที่สำเร็จcontentOffsetแล้ว โอกาสเดียวของเราคือมีส่วนร่วมในเฟสเลย์เอาต์ (โดยแทนที่layoutSubviewsการเรียกดูสโครลที่แต่ละเฟรมของมุมมองสโครล) หรือเพื่อตอบสนองต่อdidScrollวิธีการของตัวแทนที่เรียกว่าแต่ละครั้งที่contentOffsetมีการแก้ไข มาลองอันนี้กัน

การดำเนินการแปล

เราเพิ่มผู้ได้รับมอบหมายให้กับเราOverlayVCเพื่อจัดส่งกิจกรรมของ scrollview ไปยังผู้แปลของเราOverlayContainerViewController:

protocol OverlayViewControllerDelegate: class {
    func scrollViewDidScroll(_ scrollView: UIScrollView)
    func scrollViewDidStopScrolling(_ scrollView: UIScrollView)
}

class OverlayViewController: UIViewController {

    [...]

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        delegate?.scrollViewDidScroll(scrollView)
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        delegate?.scrollViewDidStopScrolling(scrollView)
    }
}

ในคอนเทนเนอร์ของเราเราติดตามการแปลโดยใช้ enum:

enum OverlayInFlightPosition {
    case minimum
    case maximum
    case progressing
}

การคำนวณตำแหน่งปัจจุบันดูเหมือนว่า:

private var overlayInFlightPosition: OverlayInFlightPosition {
    let height = translatedViewHeightContraint.constant
    if height == maximumHeight {
        return .maximum
    } else if height == minimumHeight {
        return .minimum
    } else {
        return .progressing
    }
}

เราต้องการ 3 วิธีในการจัดการการแปล:

คนแรกบอกเราว่าเราต้องเริ่มการแปลหรือไม่

private func shouldTranslateView(following scrollView: UIScrollView) -> Bool {
    guard scrollView.isTracking else { return false }
    let offset = scrollView.contentOffset.y
    switch overlayInFlightPosition {
    case .maximum:
        return offset < 0
    case .minimum:
        return offset > 0
    case .progressing:
        return true
    }
}

คนที่สองทำการแปล จะใช้translation(in:)วิธีการท่าทางเลื่อนของ scrollView

private func translateView(following scrollView: UIScrollView) {
    scrollView.contentOffset = .zero
    let translation = translatedViewTargetHeight - scrollView.panGestureRecognizer.translation(in: view).y
    translatedViewHeightContraint.constant = max(
        Constant.minimumHeight,
        min(translation, Constant.maximumHeight)
    )
}

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

private func animateTranslationEnd() {
    let position: OverlayPosition =  // ... calculation based on the current overlay position & velocity
    moveOverlay(to: position)
}

การดำเนินการมอบหมายของโฆษณาซ้อนทับของเรานั้นดูเหมือนว่า:

class OverlayContainerViewController: UIViewController {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard shouldTranslateView(following: scrollView) else { return }
        translateView(following: scrollView)
    }

    func scrollViewDidStopScrolling(_ scrollView: UIScrollView) {
        // prevent scroll animation when the translation animation ends
        scrollView.isEnabled = false
        scrollView.isEnabled = true
        animateTranslationEnd()
    }
}

ปัญหาขั้นสุดท้าย: การส่งมอบการสัมผัสของภาชนะซ้อนทับ

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

class PassThroughView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self {
            return nil
        }
        return view
    }
}

มันลบตัวเองออกจากห่วงโซ่การตอบกลับ

ในOverlayContainerViewController:

override func loadView() {
    view = PassThroughView()
}

ผลลัพธ์

นี่คือผลลัพธ์:

ผลลัพธ์

คุณสามารถค้นหารหัสที่นี่

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

อัปเดต 23/08/18

เราสามารถแทนที่scrollViewDidEndDraggingด้วย willEndScrollingWithVelocityมากกว่าenabling/ disablingเลื่อนเมื่อปลายผู้ใช้ลาก:

func scrollView(_ scrollView: UIScrollView,
                willEndScrollingWithVelocity velocity: CGPoint,
                targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    switch overlayInFlightPosition {
    case .maximum:
        break
    case .minimum, .progressing:
        targetContentOffset.pointee = .zero
    }
    animateTranslationEnd(following: scrollView)
}

เราสามารถใช้แอนิเมชั่นสปริงและอนุญาตให้ผู้ใช้โต้ตอบในขณะที่แอนิเมชั่นเพื่อทำให้การเคลื่อนไหวดีขึ้น:

func moveOverlay(to position: OverlayPosition,
                 duration: TimeInterval,
                 velocity: CGPoint) {
    overlayPosition = position
    translatedViewHeightContraint.constant = translatedViewTargetHeight
    UIView.animate(
        withDuration: duration,
        delay: 0,
        usingSpringWithDamping: velocity.y == 0 ? 1 : 0.6,
        initialSpringVelocity: abs(velocity.y),
        options: [.allowUserInteraction],
        animations: {
            self.view.layoutIfNeeded()
    }, completion: nil)
}

วิธีการนี้ไม่ได้มีการโต้ตอบระหว่างขั้นตอนการเคลื่อนไหว ตัวอย่างเช่นในแผนที่ฉันสามารถใช้นิ้วชี้นิ้วไปมาขณะที่กำลังขยาย ฉันสามารถขัดแอนิเมชั่นโดยการเลื่อนขึ้นหรือลง มันจะกลับไปยังตำแหน่งที่ใกล้เคียงที่สุด ฉันเชื่อว่าสิ่งนี้สามารถแก้ไขได้โดยใช้UIViewPropertyAnimator(ซึ่งมีความสามารถในการหยุดชั่วคราว) จากนั้นใช้fractionCompleteคุณสมบัติเพื่อทำการขัดผิว
สิงหาคม

1
คุณถูกต้อง @aust ฉันลืมที่จะผลักดันการเปลี่ยนแปลงก่อนหน้าของฉัน มันควรจะดีตอนนี้ ขอบคุณ
GaétanZ

นี่เป็นความคิดที่ดี แต่ฉันสามารถเห็นปัญหาหนึ่งได้ทันที ในtranslateView(following:)วิธีการที่คุณคำนวณความสูงขึ้นอยู่กับการแปล แต่ที่อาจเริ่มต้นจากค่าที่แตกต่างกันกว่า 0.0 (เช่นมุมมองตารางเลื่อนนิด ๆ หน่อย ๆ และซ้อนทับเป็น maximized เมื่อคุณเริ่มลาก) นั่นจะส่งผลให้การกระโดดครั้งแรก คุณสามารถแก้ปัญหานี้ได้โดยการscrollViewWillBeginDraggingโทรกลับที่คุณจะจำcontentOffsetมุมมองเริ่มต้นของตารางและใช้ในการtranslateView(following:)คำนวณของ
Jakub Truhlář

1
@ GaétanZฉันสนใจแนบ debugger ไปยัง Maps.app บน Simulator ด้วย แต่ได้รับข้อผิดพลาด "ไม่สามารถแนบ pid:" 9276 "" แล้วตามด้วย "ตรวจสอบ" Maps "ไม่ได้ทำงานอยู่และ <username> ได้รับอนุญาตให้ แก้ไขข้อบกพร่อง " - คุณหลอก Xcode ให้แนบ Maps.app ได้อย่างไร?
matm

1
@matm @ GaétanZทำงานให้ฉันด้วย Xcode 10.1 / Mojave -> เพียงแค่ต้องปิดการใช้งานเพื่อปิดการใช้งานการป้องกันความสมบูรณ์ของระบบ (SIP) คุณต้อง: 1) รีสตาร์ท mac ของคุณ; 2) กด cmd + R ค้างไว้ 3) จากเมนูให้เลือก Utilities จากนั้นเลือก Terminal; 4) csrutil disable && rebootในหน้าต่างชนิดเทอร์มิ: จากนั้นคุณจะมีพลังทั้งหมดที่รูทควรให้คุณรวมถึงความสามารถในการแนบกับกระบวนการ Apple
valdyr

18

ลองลูกรอก :

Pulley เป็นไลบรารีลิ้นชักที่ใช้งานง่ายซึ่งมีวัตถุประสงค์เพื่อเลียนแบบลิ้นชักในแอป Maps ของ iOS 10 มันเปิดเผย API ง่าย ๆ ที่อนุญาตให้คุณใช้คลาสย่อย UIViewController ใด ๆ เป็นเนื้อหา drawer หรือเนื้อหาหลัก

พรีวิว Pulley

https://github.com/52inc/Pulley


1
มันรองรับการเลื่อนในรายการพื้นฐานหรือไม่?
Richard Lindhout

@RichardLindhout - หลังจากรันการสาธิตแล้วดูเหมือนว่ามันรองรับการเลื่อน แต่ไม่ใช่การเปลี่ยนแปลงที่ราบรื่นจากการเลื่อนลิ้นชักไปยังการเลื่อนรายการ
Stoph

ตัวอย่างปรากฏขึ้นเพื่อซ่อนลิงก์ทางกฎหมายของแอปเปิ้ลที่ด้านล่างซ้าย
Gerd Castan

1
คำถามง่ายๆสุด ๆ คุณจะเห็นดัชนีที่ด้านบนของแผ่นด้านล่างได้อย่างไร
Israfil

12

คุณสามารถลองคำตอบของฉันhttps://github.com/SCENEE/FloatingPanel มันมีตัวควบคุมมุมมองภาชนะเพื่อแสดงอินเตอร์เฟซ "แผ่นด้านล่าง"

ใช้งานง่ายและคุณไม่ต้องกังวลว่าจะใช้ตัวจดจำลายเส้นใด ๆ ! นอกจากนี้คุณสามารถติดตามมุมมองเลื่อน (หรือมุมมองพี่น้อง) ในแผ่นด้านล่างหากจำเป็น

นี่เป็นตัวอย่างง่ายๆ โปรดทราบว่าคุณจำเป็นต้องจัดเตรียมตัวควบคุมมุมมองเพื่อแสดงเนื้อหาของคุณในแผ่นด้านล่าง

import UIKit
import FloatingPanel

class ViewController: UIViewController {
    var fpc: FloatingPanelController!

    override func viewDidLoad() {
        super.viewDidLoad()

        fpc = FloatingPanelController()

        // Add "bottom sheet" in self.view.
        fpc.add(toParent: self)

        // Add a view controller to display your contents in "bottom sheet".
        let contentVC = ContentViewController()
        fpc.set(contentViewController: contentVC)

        // Track a scroll view in "bottom sheet" content if needed.
        fpc.track(scrollView: contentVC.tableView)    
    }
    ...
}

นี่คืออีกตัวอย่างรหัสที่จะแสดงแผ่นด้านล่างเพื่อค้นหาสถานที่เช่น Apple Maps

แอพตัวอย่างเช่น Apple Maps


fpc.set (contentViewController: newsVC) วิธีการที่ขาดหายไปในห้องสมุด
Faris Muhammed

1
คำถามง่ายๆสุด ๆ คุณจะเห็นดัชนีที่ด้านบนของแผ่นด้านล่างได้อย่างไร
Israfil

1
@ AIsrafil ฉันคิดว่ามันเป็นเพียง UIView
ShadeToD

ฉันคิดว่ามันเป็นทางออกที่ดีจริงๆ แต่มีปัญหาใน iOS 13 ด้วยการนำเสนอตัวควบคุมมุมมอง modal กับพาเนลลอยนี้
ShadeToD

11

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

ดูลิงค์ด้านล่างสำหรับรายละเอียดเพิ่มเติม

https://github.com/OfTheWolf/UBottomSheet

แผ่น ubottom


นี่ควรเป็นคำตอบที่เลือกเนื่องจากคุณยังสามารถคลิกที่ VC ด้านหลังป๊อปอัป
Jay

คำถามง่ายๆสุด ๆ คุณจะเห็นดัชนีที่ด้านบนของแผ่นด้านล่างได้อย่างไร
Israfil

1

ฉันเพิ่งสร้างส่วนประกอบที่เรียกว่าSwipeableViewเป็นคลาสย่อยของUIViewซึ่งเขียนใน Swift 5.1 รองรับทั้ง 4 ทิศทางมีตัวเลือกการปรับแต่งที่หลากหลายและสามารถเคลื่อนไหวและแก้ไขคุณสมบัติและรายการที่แตกต่างกัน (เช่นข้อ จำกัด โครงร่างสีพื้นหลัง / สีอ่อนการแปลงเลียนแบบช่องอัลฟาและศูนย์ดูทั้งหมดสาธิตด้วยเคสที่เกี่ยวข้อง) นอกจากนี้ยังสนับสนุนการประสานการปัดด้วยมุมมองเลื่อนด้านในหากตั้งค่าหรือตรวจจับอัตโนมัติ ควรใช้งานง่ายและตรงไปตรงมา (ฉันหวังว่า🙂)

เชื่อมโยงที่https://github.com/LucaIaco/SwipeableView

พิสูจน์แนวคิด:

ป้อนคำอธิบายรูปภาพที่นี่

หวังว่ามันจะช่วย


0

บางทีคุณสามารถลองใช้คำตอบของฉันhttps://github.com/AnYuan/AYPannelซึ่งเป็นแรงบันดาลใจจาก Pulley ราบรื่นจากการย้ายลิ้นชักเพื่อเลื่อนรายการ ฉันเพิ่มรูปแบบการเลื่อนบนมุมมองเลื่อนคอนเทนเนอร์และตั้งค่าควรจดจำพร้อมกันด้วย GestureRecognizer เพื่อส่งคืน YES รายละเอียดเพิ่มเติมในลิงค์ GitHub ของฉันด้านบน ต้องการความช่วยเหลือ


6
ในขณะที่ลิงค์นี้อาจตอบคำถามได้ดีกว่าที่จะรวมส่วนที่สำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง ในขณะที่ลิงค์นี้อาจตอบคำถามได้ดีกว่าที่จะรวมส่วนที่สำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง
pirho
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.