2

私は画面上に 9 つのビューを持つアプリに取り組んでおり、ユーザーが望む方法でビューを接続し、シーケンスをパスワードとして記録することを望んでいます。しかし、どのジェスチャ認識エンジンを使用すればよいかわかりません。

CMUnistrokeGestureRecognizer または複数のスワイプ ジェスチャの組み合わせなどを使用する必要がありますか? ありがとう。

4

2 に答える 2

4

UIPanGestureRecognizer次のようなものを使用できます。

CGFloat const kMargin = 10;

- (void)viewDidLoad
{
    [super viewDidLoad];

    // create a container view that all of our subviews for which we want to detect touches are:

    CGFloat containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - kMargin * 2.0;

    UIView *container = [[UIView alloc] initWithFrame:CGRectMake(kMargin, kMargin, containerWidth, containerWidth)];
    container.backgroundColor = [UIColor darkGrayColor];
    [self.view addSubview:container];

    // now create all of the subviews, specifying a tag for each; and

    CGFloat cellWidth = (containerWidth - (4.0 * kMargin)) / 3.0;

    for (NSInteger column = 0; column < 3; column++)
    {
        for (NSInteger row = 0; row < 3; row++)
        {
            UIView *cell = [[UIView alloc] initWithFrame:CGRectMake(kMargin + column * (cellWidth + kMargin),
                                                                    kMargin + row    * (cellWidth + kMargin),
                                                                    cellWidth, cellWidth)];
            cell.tag = row * 3 + column;
            cell.backgroundColor = [UIColor lightGrayColor];
            [container addSubview:cell];
        }
    }

    // finally, create the gesture recognizer

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(handlePan:)];
    [container addGestureRecognizer:pan];
}

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static NSMutableArray *gesturedSubviews;

    // if we're starting a gesture, initialize our list of subviews that we've gone over

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        gesturedSubviews = [NSMutableArray array];
    }

    // now figure out whether:
    //   (a) are we over a subview; and
    //   (b) is this a different subview than we last were over

    CGPoint location = [gesture locationInView:gesture.view];

    for (UIView *subview in gesture.view.subviews)
    {
        if (CGRectContainsPoint(subview.frame, location))
        {
            if (subview != [gesturedSubviews lastObject])
            {
                [gesturedSubviews addObject:subview];

                // an example of the sort of graphical flourish to give the
                // some visual cue that their going over the subview in question 
                // was recognized

                [UIView animateWithDuration:0.25
                                      delay:0.0
                                    options:UIViewAnimationOptionAutoreverse
                                 animations:^{
                                     subview.alpha = 0.5;
                                 }
                                 completion:^(BOOL finished){
                                     subview.alpha = 1.0;
                                 }];
            }
        }
    }

    // finally, when done, let's just log the subviews
    // you would do whatever you would want here

    if (gesture.state == UIGestureRecognizerStateEnded)
    {
        NSLog(@"We went over:");

        for (UIView *subview in gesturedSubviews)
        {
            NSLog(@"  %d", subview.tag);
        }

        // you might as well clean up your static variable when you're done

        gesturedSubviews = nil;
    }
}

明らかに、好きな方法でサブビューを作成し、好きな方法でそれらを追跡しますが、アイデアは、一意のtag番号を持つサブビューを持つことであり、ジェスチャー認識エンジンは、単一のジェスチャーでそれらをどの順序で通過するかを確認するだけです. .

必要なものを正確にキャプチャできなかったとしても、少なくとも、パン ジェスチャ レコグナイザーを使用して、あるサブビューから別のサブビューへの指の動きを追跡する方法を示しています。


アップデート:

ユーザーがサインインしているときに画面にパスを描画したい場合はCAShapeLayerUIBezierPath. 以下にそれを示しますが、警告として、これは優れたセキュリティ機能ではない可能性があることを指摘しなければならないと感じています: 通常、パスワード入力では、ユーザーが行っていることを確認できるように十分に表示します。しかし、誰かが肩越しに一瞥してパスワード全体が何であるかを確認できるほどには十分ではありません。テキスト パスワードを入力すると、通常、iOS は最後に打ったキーを一時的に表示しますが、すぐにそれをアスタリスクに変えるため、パスワード全体を見ることはできません。したがって、私の最初の提案。

しかし、ユーザーがパスを描いたときにそのパスを表示することに本気で取り組んでいる場合は、次のようなものを使用できます。まず、これにはQuartz 2Dが必要です。QuartzCore.frameworkしたがって、プロジェクトに を追加します (ライブラリまたはフレームワークへのリンクを参照してください)。次に、QuartCore ヘッダーをインポートします。

#import <QuartzCore/QuartzCore.h>

3 番目に、パン ハンドラーを次のようなものに置き換えます。

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static NSMutableArray *gesturedSubviews;
    static UIBezierPath *path = nil;
    static CAShapeLayer *shapeLayer = nil;

    // if we're starting a gesture, initialize our list of subviews that we've gone over

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        gesturedSubviews = [NSMutableArray array];
    }

    // now figure out whether:
    //   (a) are we over a subview; and
    //   (b) is this a different subview than we last were over

    CGPoint location = [gesture locationInView:gesture.view];

    for (UIView *subview in gesture.view.subviews)
    {
        if (!path)
        {
            // if the path hasn't be started, initialize it and the shape layer

            path = [UIBezierPath bezierPath];
            [path moveToPoint:location];
            shapeLayer = [[CAShapeLayer alloc] init];
            shapeLayer.strokeColor = [UIColor redColor].CGColor;
            shapeLayer.fillColor = [UIColor clearColor].CGColor;
            shapeLayer.lineWidth = 2.0;
            [gesture.view.layer addSublayer:shapeLayer];
        }
        else
        {
            // otherwise add this point to the layer's path

            [path addLineToPoint:location];
            shapeLayer.path = path.CGPath;
        }

        if (CGRectContainsPoint(subview.frame, location))
        {
            if (subview != [gesturedSubviews lastObject])
            {
                [gesturedSubviews addObject:subview];

                [UIView animateWithDuration:0.25
                                      delay:0.0
                                    options:UIViewAnimationOptionAutoreverse
                                 animations:^{
                                     subview.alpha = 0.5;
                                 }
                                 completion:^(BOOL finished){
                                     subview.alpha = 1.0;
                                 }];
            }
        }
    }

    // finally, when done, let's just log the subviews
    // you would do whatever you would want here

    if (gesture.state == UIGestureRecognizerStateEnded)
    {
        // assuming the tags are numbers between 0 and 9 (inclusive), we can build the password here

        NSMutableString *password = [NSMutableString string];

        for (UIView *subview in gesturedSubviews)
            [password appendFormat:@"%c", subview.tag + 48];

        NSLog(@"Password = %@", password);

        // clean up our array of gesturedSubviews

        gesturedSubviews = nil;

        // clean up the drawing of the path on the screen the user drew

        [shapeLayer removeFromSuperlayer];
        shapeLayer = nil;
        path = nil;
    }
}

これにより、ジェスチャが進むにつれてユーザーが描くパスが生成されます。

ユーザーの指のパス

ユーザーの指のすべての動きでユーザーが描くパスを表示するのではなく、次のようにサブビューの中心の間に線を引くだけかもしれません。

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    static NSMutableArray *gesturedSubviews;
    static UIBezierPath *path = nil;
    static CAShapeLayer *shapeLayer = nil;

    // if we're starting a gesture, initialize our list of subviews that we've gone over

    if (gesture.state == UIGestureRecognizerStateBegan)
    {
        gesturedSubviews = [NSMutableArray array];
    }

    // now figure out whether:
    //   (a) are we over a subview; and
    //   (b) is this a different subview than we last were over

    CGPoint location = [gesture locationInView:gesture.view];

    for (UIView *subview in gesture.view.subviews)
    {
        if (CGRectContainsPoint(subview.frame, location))
        {
            if (subview != [gesturedSubviews lastObject])
            {
                [gesturedSubviews addObject:subview];

                if (!path)
                {
                    // if the path hasn't be started, initialize it and the shape layer

                    path = [UIBezierPath bezierPath];
                    [path moveToPoint:subview.center];
                    shapeLayer = [[CAShapeLayer alloc] init];
                    shapeLayer.strokeColor = [UIColor redColor].CGColor;
                    shapeLayer.fillColor = [UIColor clearColor].CGColor;
                    shapeLayer.lineWidth = 2.0;
                    [gesture.view.layer addSublayer:shapeLayer];
                }
                else
                {
                    // otherwise add this point to the layer's path

                    [path addLineToPoint:subview.center];
                    shapeLayer.path = path.CGPath;
                }

                [UIView animateWithDuration:0.25
                                      delay:0.0
                                    options:UIViewAnimationOptionAutoreverse
                                 animations:^{
                                     subview.alpha = 0.5;
                                 }
                                 completion:^(BOOL finished){
                                     subview.alpha = 1.0;
                                 }];
            }
        }
    }

    // finally, when done, let's just log the subviews
    // you would do whatever you would want here

    if (gesture.state == UIGestureRecognizerStateEnded)
    {
        // assuming the tags are numbers between 0 and 9 (inclusive), we can build the password here

        NSMutableString *password = [NSMutableString string];

        for (UIView *subview in gesturedSubviews)
            [password appendFormat:@"%c", subview.tag + 48];

        NSLog(@"Password = %@", password);

        // clean up our array of gesturedSubviews

        gesturedSubviews = nil;

        // clean up the drawing of the path on the screen the user drew

        [shapeLayer removeFromSuperlayer];
        shapeLayer = nil;
        path = nil;
    }
}

それは次のようなものをもたらします:

線のあるユーザー パス

あらゆる種類のオプションがありますが、独自のソリューションを設計できるように構成要素が揃っていることを願っています。

于 2013-01-04T17:39:31.307 に答える
0

ロブ、ここで純粋な盗作を許してください:) Swift 3.0で同じコードが必要だった:)だから、あなたが書いたこの素晴らしい小さな例をSwift 3.0に翻訳しました。

ViewController.swift

import UIKit

class ViewController: UIViewController {

static let kMargin:CGFloat = 10.0;

override func viewDidLoad()
{
super.viewDidLoad()

// create a container view that all of our subviews for which we want to detect touches are:

let containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - ViewController.kMargin * 2.0

let container = UIView(frame: CGRect(x: ViewController.kMargin, y: ViewController.kMargin, width: containerWidth, height: containerWidth))
container.backgroundColor = UIColor.darkGray
    view.addSubview(container)

// now create all of the subviews, specifying a tag for each; and

let cellWidth = (containerWidth - (4.0 * ViewController.kMargin)) / 3.0

    for column in 0 ..< 3 {
        for row in 0 ..< 3 {
            let cell = UIView(frame: CGRect(x: ViewController.kMargin + CGFloat(column) * (cellWidth + ViewController.kMargin), y: ViewController.kMargin + CGFloat(row) * (cellWidth + ViewController.kMargin), width: cellWidth, height: cellWidth))
            cell.tag = row * 3 + column;
            container.addSubview(cell)
        }
    }

// finally, create the gesture recognizer

    let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
    container.addGestureRecognizer(pan)
}


func handlePan(gesture: UIPanGestureRecognizer)
{
    var  gesturedSubviews : [UIView] = []

// if we're starting a gesture, initialize our list of subviews that we've gone over

if (gesture.state == .began)
{
    gesturedSubviews.removeAll()
}

    let location = gesture.location(in: gesture.view)
    for subview in (gesture.view?.subviews)! {
        if (subview.frame.contains(location)) {
            if (subview != gesturedSubviews.last) {
            gesturedSubviews.append(subview)
                subview.backgroundColor = UIColor.blue
        }
    }

// finally, when done, let's just log the subviews
// you would do whatever you would want here

if (gesture.state != .recognized)
{
print("We went over:");

for subview in gesturedSubviews {
            print(" %d", (subview as AnyObject).tag);
}

// you might as well clean up your static variable when you're done

}
}
}

}

更新:ほぼそうです。私もアップデートを翻訳しようとしましたが、私の翻訳は何かを見逃していてうまくいきませんでした。

import UIKit
import QuartzCore

class ViewController: UIViewController {

static let kMargin:CGFloat = 10.0;

override func viewDidLoad()
{
super.viewDidLoad()

// create a container view that all of our subviews for which we want to detect touches are:

let containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - ViewController.kMargin * 2.0

let container = UIView(frame: CGRect(x: ViewController.kMargin, y: ViewController.kMargin, width: containerWidth, height: containerWidth))
container.backgroundColor = UIColor.darkGray
    view.addSubview(container)

// now create all of the subviews, specifying a tag for each; and

let cellWidth = (containerWidth - (4.0 * ViewController.kMargin)) / 3.0

    for column in 0 ..< 3 {
        for row in 0 ..< 3 {
            let cell = UIView(frame: CGRect(x: ViewController.kMargin + CGFloat(column) * (cellWidth + ViewController.kMargin), y: ViewController.kMargin + CGFloat(row) * (cellWidth + ViewController.kMargin), width: cellWidth, height: cellWidth))
            cell.tag = row * 3 + column;
            container.addSubview(cell)
        }
    }

// finally, create the gesture recognizer

    let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
    container.addGestureRecognizer(pan)

}

// Here's a Swift 3.0 version based on Rajesh Choudhary's answer:

func drawLine(onLayer layer: CALayer, fromPoint start: CGPoint, toPoint end:CGPoint) {
    let line = CAShapeLayer()
    let linePath = UIBezierPath()
    linePath.move(to: start)
    linePath.addLine(to: end)
    line.path = linePath.cgPath
    line.fillColor = nil
    line.lineWidth = 8
    line.opacity = 0.5
    line.strokeColor = UIColor.red.cgColor
    layer.addSublayer(line)
}

var gesturedSubviews : [UIView] = []
var startX: CGFloat!
var startY: CGFloat!
var endX: CGFloat!
var endY: CGFloat!

func handlePan(gesture: UIPanGestureRecognizer) {

if (gesture.state == .began)
{
    gesturedSubviews = [];
    let location = gesture.location(in: gesture.view)
    print("location \(location)")
    startX = location.x
    startY = location.y
  }
  if (gesture.state == .changed) {
    let location = gesture.location(in: gesture.view)
    print("location \(location)")
    endX = location.x
    endY = location.y
    drawLine(onLayer: view.layer, fromPoint: CGPoint(x: startX, y: startY), toPoint: CGPoint(x:endX, y:endY))
    startX = endX
    startY = endY
}
if (gesture.state == .ended) {
        let location = gesture.location(in: gesture.view)
        print("location \(location)")
        drawLine(onLayer: view.layer, fromPoint: CGPoint(x: startX, y: startY), toPoint: CGPoint(x:location.x, y:location.y))
}

// now figure out whether:
//   (a) are we over a subview; and
//   (b) is this a different subview than we last were over

 let location = gesture.location(in: gesture.view)
    print("location \(location)")

    for subview in (gesture.view?.subviews)! {
        if  subview.frame.contains(location) {
                if (subview != gesturedSubviews.last) {

                gesturedSubviews.append(subview)

                subview.backgroundColor = UIColor.blue
                subview.alpha = 1.0

            }
        }
    }
}

}
于 2016-10-15T15:25:54.333 に答える