奇妙な構成のこのアプリを実行する必要があります。
次の画像に示すように、メインビューはUIScrollViewです。次に、内部にUIPageViewがあり、PageViewの各ページにUITableViewがあります。
これまでにこれをすべて実行しました。しかし、私の問題は、behavenaturallyへのスクロールが必要なことです。
次は私が意味することです自然に。現在、UITableViewの1つをスクロールすると、スクロールビューではなくテーブルビューがスクロールされます。しかし、ScrollViewがスクロールできないためにScrollViewをスクロールさせて、最上部または最下部に移動させたい場合(その場合、TableViewをスクロールさせたい)。
たとえば、スクロールビューが現在上部にスクロールされているとします。次に、(表示されている現在のページの)テーブルビューに指を置き、スクロールを開始しますdown。この場合、私はscrollviewをスクロールさせます(tableviewなし)。スクロールビューを下にスクロールし続けて下部に到達した場合、ディスプレイから指を離してtebleviewの上に戻し、再度下にスクロールすると、ScrollViewが下部に到達したため、TableViewを今すぐ下にスクロールしたいスクロールを続けることができます。
このスクロールを実装する方法についてのアイデアはありますか?
私はこれで本当に迷っています。どんな助けでも大歓迎です:(
ありがとう!
スクロールビューとテーブルビューを同時に処理するソリューションは、UIScrollViewDelegate
を中心に展開します。したがって、View Controllerをそのプロトコルに適合させてください。
class ViewController: UIViewController, UIScrollViewDelegate {
スクロールビューとテーブルビューをアウトレットとして表します。
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
また、画面の高さだけでなく、スクロールビューのコンテンツの高さも追跡する必要があります。理由は後でわかります。
let screenHeight = UIScreen.mainScreen().bounds.height
let scrollViewContentHeight = 1200 as CGFloat
viewDidLoad:
には少し設定が必要です:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)
scrollView.delegate = self
tableView.delegate = self
scrollView.bounces = false
tableView.bounces = false
tableView.scrollEnabled = false
}
シンプルにするためにバウンスをオフにしました。 キー設定は、スクロールビューとテーブルビューのデリゲートであり、最初はテーブルビューのスクロールをオフにします。
これらは、scrollViewDidScroll:
デリゲートメソッドがスクロールビューの下部に到達し、テーブルビューの上部に到達することを処理できるようにするために必要です。その方法は次のとおりです。
func scrollViewDidScroll(scrollView: UIScrollView) {
let yOffset = scrollView.contentOffset.y
if scrollView == self.scrollView {
if yOffset >= scrollViewContentHeight - screenHeight {
scrollView.scrollEnabled = false
tableView.scrollEnabled = true
}
}
if scrollView == self.tableView {
if yOffset <= 0 {
self.scrollView.scrollEnabled = true
self.tableView.scrollEnabled = false
}
}
}
デリゲートメソッドが行うことは、スクロールビューが最下部に到達したことを検出することです。その場合、テーブルビューをスクロールできます。また、スクロールビューが再度有効になっているテーブルビューが上部に到達したことも検出します。
結果を示すためにGIFを作成しました。
ダニエルの答えを修正して、より効率的でバグのないものにしました。
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeight: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
//Set table height to cover entire view
//if navigation bar is not translucent, reduce navigation bar height from view height
tableHeight.constant = self.view.frame.height-64
self.tableView.isScrollEnabled = false
//no need to write following if checked in storyboard
self.scrollView.bounces = false
self.tableView.bounces = true
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 20
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))
label.text = "Section 1"
label.textAlignment = .center
label.backgroundColor = .yellow
return label
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Row: \(indexPath.row+1)"
return cell
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView == self.scrollView {
tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)
}
if scrollView == self.tableView {
self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
}
}
完全なプロジェクトはここで見ることができます: https://gitlab.com/vineetks/TableScroll.git
多くの試行錯誤の後、これは私にとって最もうまくいったものです。このソリューションでは、2つのニーズを解決する必要があります。1)誰がスクロールプロパティを使用するかを決定します。 tableViewまたはscrollView? 2)tableViewがテーブル/コンテンツの最上部に到達するまでscrollViewに権限を与えないことを確認してください。
Scrollviewをスクロールとtableviewのどちらに使用するかを確認するために、tableviewのすぐ上のUIViewがフレーム内にあるかどうかを確認しました。 UIViewがフレーム内にある場合、scrollViewにはスクロールする権限が必要であると言っても安全です。 UIViewがフレーム内にない場合、tableViewがウィンドウ全体を占有しているため、スクロールする権限が必要です。
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.bounds.intersects(UIView.frame) == true {
//the UIView is within frame, use the UIScrollView's scrolling.
if tableView.contentOffset.y == 0 {
//tableViews content is at the top of the tableView.
tableView.isUserInteractionEnabled = false
tableView.resignFirstResponder()
print("using scrollView scroll")
} else {
//UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
} else {
//UIView is not in frame. Use tableViews scroll.
tableView.isUserInteractionEnabled = true
scrollView.resignFirstResponder()
print("using tableView scroll")
}
}
これが誰かを助けることを願っています!
ここでの答えはどれも私にとって完璧に機能しませんでした。それぞれに独自の微妙な問題がありました(1つのスクロールビューが下になったときに繰り返しスワイプするか、スクロールインジケーターが正しく表示されないなど)。
Ole Begemannがこれを正確に行うことについて素晴らしい記事を書いています https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/
古い投稿であるにもかかわらず、概念は現在のAPIにも適用されます。さらに、彼のアプローチのObjective-C実装が維持されています(Xcode 9互換) https://github.com/eyeem/OLEContainerScrollView
2つの選択肢があると思います。
スクロールビューとメインビューのサイズがわかっているため、スクロールビューが下にあるかどうかはわかりません。
if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
// reach bottom
}
ヒットしたとき;基本的に設定します
[contentScrollView setScrollEnabled:NO];
tableViewのその他の方法。
もう1つは、より正確に考えると、ビューにジェスチャーを追加することです。
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];
// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;
// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];
// Do any additional setup after loading the view, typically from a nib
そのため、ジェスチャーを追加するときに、setScrollEnabled
のrespondToTapGesture
を変更することで、アクティブビューを簡単に制御できます。
私もこの問題に苦労していました。非常に簡単な解決策があります。
インターフェイスビルダーで:
さて、ここからがおもしろい部分です。追加するページごとに、tableViewの上部からのオフセットを確認してください。それでおしまい。たとえば、このコードを使用すると、次のことができます(UITableViewControllerをページとして使用すると仮定します)。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let tables = viewControllers.compactMap { $0 as? UITableViewController }
tables.forEach {
$0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0)
$0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height)
}
}
テーブルビュー内のスクロール内の乱雑なスクロール、デリゲートとのマングリング、重複したスクロール、完全に自然な動作はありません。ヘッダーが表示されない場合は、おそらくtableViewの背景色が原因です。 tableViewの下からヘッダーを表示するには、クリアに設定する必要があります。
素晴らしいライブラリを見つけました MXParallaxHeader
StoryboardでUIScrollView
クラスをMXScrollView
に設定すると、魔法が発生します。
UIScrollView
コンテナビューを埋め込むときに、このクラスを使用してUIPageViewController
を処理しました。さらに詳細に視差ヘッダービューを挿入することもできます。
また、このライブラリはCocoapods
およびCarthage
を提供します
以下にUIView
Hierarchyを表す画像を添付しました。 MXScrollView階層
正解としてマークされたソリューションを試しましたが、正しく機能しませんでした。ユーザーはスクロールのためにテーブルビューを2回クリックする必要があり、その後、画面全体を再度スクロールすることはできませんでした。したがって、viewDidLoad()で次のコードを適用しました。
tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped)))
scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))
そして、以下のコードはアクションの実装です:
func tableViewSwiped(){
scrollView.isScrollEnabled = false
tableView.isScrollEnabled = true
}
func scrollViewSwiped(){
scrollView.isScrollEnabled = true
tableView.isScrollEnabled = false
}
たぶんブルートフォースですが、完全に機能しています。ところで、自動レイアウトを使用しています。
tableView(またはcollectionViewなど)の場合、ストーリーボードで任意の高さを設定し、クラスへのアウトレットを作成します。必要に応じて、(viewDidLoad()または...)tableViewをスクロールする必要がないように、tableViewの高さを十分に大きく設定します。 (行数を事前に知る必要があります)そうすると、外側のscrollViewのみがうまくスクロールします。